Initial commit
This commit is contained in:
15
packages/ui/.storybook/main.ts
Normal file
15
packages/ui/.storybook/main.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { StorybookConfig } from '@storybook/nextjs-vite';
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ['../src/**/*.stories.@(ts|tsx)'],
|
||||
addons: ['@storybook/addon-docs'],
|
||||
framework: {
|
||||
name: '@storybook/nextjs-vite',
|
||||
options: {},
|
||||
},
|
||||
docs: {
|
||||
autodocs: 'tag',
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
18
packages/ui/.storybook/preview.ts
Normal file
18
packages/ui/.storybook/preview.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { Preview } from '@storybook/react';
|
||||
import '../src/tokens/tokens.css';
|
||||
|
||||
const preview: Preview = {
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
backgrounds: {
|
||||
default: 'page',
|
||||
values: [
|
||||
{ name: 'page', value: '#f8f9fa' },
|
||||
{ name: 'surface', value: '#ffffff' },
|
||||
{ name: 'dark', value: '#212529' },
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default preview;
|
||||
3
packages/ui/eslint.config.js
Normal file
3
packages/ui/eslint.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import { react } from '@dwellops/config/eslint';
|
||||
|
||||
export default react;
|
||||
47
packages/ui/package.json
Normal file
47
packages/ui/package.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "@dwellops/ui",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./src/index.ts",
|
||||
"default": "./src/index.ts"
|
||||
},
|
||||
"./tokens": "./src/tokens/tokens.css",
|
||||
"./Button": {
|
||||
"types": "./src/Button/Button.tsx",
|
||||
"default": "./src/Button/Button.tsx"
|
||||
},
|
||||
"./Card": {
|
||||
"types": "./src/Card/Card.tsx",
|
||||
"default": "./src/Card/Card.tsx"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "eslint src",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"storybook:build": "storybook build"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"storybook": "catalog:",
|
||||
"@storybook/nextjs-vite": "catalog:",
|
||||
"@storybook/addon-docs": "catalog:",
|
||||
"@storybook/react": "catalog:",
|
||||
"eslint": "catalog:",
|
||||
"typescript-eslint": "catalog:",
|
||||
"eslint-plugin-react": "catalog:",
|
||||
"eslint-plugin-react-hooks": "catalog:",
|
||||
"eslint-plugin-jsx-a11y": "catalog:",
|
||||
"@dwellops/config": "workspace:*"
|
||||
}
|
||||
}
|
||||
150
packages/ui/src/Button/Button.module.css
Normal file
150
packages/ui/src/Button/Button.module.css
Normal file
@@ -0,0 +1,150 @@
|
||||
.button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--space-2);
|
||||
border: var(--border-width-default) solid transparent;
|
||||
border-radius: var(--border-radius-base);
|
||||
font-family: var(--font-family-base);
|
||||
font-weight: var(--font-weight-medium);
|
||||
line-height: var(--line-height-tight);
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
transition:
|
||||
background-color var(--transition-fast),
|
||||
border-color var(--transition-fast),
|
||||
color var(--transition-fast),
|
||||
box-shadow var(--transition-fast);
|
||||
white-space: nowrap;
|
||||
user-select: none;
|
||||
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
|
||||
&:disabled,
|
||||
&[aria-disabled='true'] {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Variants ────────────────────────────────────────────────── */
|
||||
|
||||
.primary {
|
||||
background-color: var(--color-interactive-default);
|
||||
border-color: var(--color-interactive-default);
|
||||
color: var(--color-text-inverse);
|
||||
|
||||
&:hover:not(:disabled):not([aria-disabled='true']) {
|
||||
background-color: var(--color-interactive-hover);
|
||||
border-color: var(--color-interactive-hover);
|
||||
}
|
||||
|
||||
&:active:not(:disabled):not([aria-disabled='true']) {
|
||||
background-color: var(--color-interactive-active);
|
||||
border-color: var(--color-interactive-active);
|
||||
}
|
||||
}
|
||||
|
||||
.secondary {
|
||||
background-color: var(--color-bg-surface);
|
||||
border-color: var(--color-border-default);
|
||||
color: var(--color-text-primary);
|
||||
|
||||
&:hover:not(:disabled):not([aria-disabled='true']) {
|
||||
background-color: var(--color-bg-subtle);
|
||||
}
|
||||
}
|
||||
|
||||
.ghost {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
color: var(--color-interactive-default);
|
||||
|
||||
&:hover:not(:disabled):not([aria-disabled='true']) {
|
||||
background-color: var(--color-brand-50);
|
||||
}
|
||||
}
|
||||
|
||||
.danger {
|
||||
background-color: var(--color-error-500);
|
||||
border-color: var(--color-error-500);
|
||||
color: var(--color-text-inverse);
|
||||
|
||||
&:hover:not(:disabled):not([aria-disabled='true']) {
|
||||
background-color: var(--color-error-700);
|
||||
border-color: var(--color-error-700);
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Sizes ────────────────────────────────────────────────────── */
|
||||
|
||||
.sm {
|
||||
font-size: var(--font-size-sm);
|
||||
padding: var(--space-1) var(--space-3);
|
||||
min-height: 2rem;
|
||||
}
|
||||
|
||||
.md {
|
||||
font-size: var(--font-size-base);
|
||||
padding: var(--space-2) var(--space-4);
|
||||
min-height: 2.5rem;
|
||||
}
|
||||
|
||||
.lg {
|
||||
font-size: var(--font-size-lg);
|
||||
padding: var(--space-3) var(--space-6);
|
||||
min-height: 3rem;
|
||||
}
|
||||
|
||||
/* ── Modifiers ────────────────────────────────────────────────── */
|
||||
|
||||
.fullWidth {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.loading {
|
||||
position: relative;
|
||||
|
||||
& .label {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Sub-elements ─────────────────────────────────────────────── */
|
||||
|
||||
.icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.spinnerDot {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border: 2px solid currentcolor;
|
||||
border-top-color: transparent;
|
||||
border-radius: var(--border-radius-full);
|
||||
animation: spin 0.6s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
73
packages/ui/src/Button/Button.stories.tsx
Normal file
73
packages/ui/src/Button/Button.stories.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { Button } from './Button';
|
||||
|
||||
const meta: Meta<typeof Button> = {
|
||||
title: 'Primitives/Button',
|
||||
component: Button,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
variant: {
|
||||
control: 'select',
|
||||
options: ['primary', 'secondary', 'ghost', 'danger'],
|
||||
},
|
||||
size: {
|
||||
control: 'select',
|
||||
options: ['sm', 'md', 'lg'],
|
||||
},
|
||||
loading: { control: 'boolean' },
|
||||
disabled: { control: 'boolean' },
|
||||
fullWidth: { control: 'boolean' },
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component: `
|
||||
Core interactive button primitive. Follows WCAG 2.2 AA requirements:
|
||||
visible focus ring, aria-disabled, aria-busy states.
|
||||
`,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Button>;
|
||||
|
||||
export const Primary: Story = {
|
||||
args: { variant: 'primary', children: 'Save changes' },
|
||||
};
|
||||
|
||||
export const Secondary: Story = {
|
||||
args: { variant: 'secondary', children: 'Cancel' },
|
||||
};
|
||||
|
||||
export const Ghost: Story = {
|
||||
args: { variant: 'ghost', children: 'Learn more' },
|
||||
};
|
||||
|
||||
export const Danger: Story = {
|
||||
args: { variant: 'danger', children: 'Delete record' },
|
||||
};
|
||||
|
||||
export const Small: Story = {
|
||||
args: { size: 'sm', children: 'Small button' },
|
||||
};
|
||||
|
||||
export const Large: Story = {
|
||||
args: { size: 'lg', children: 'Large button' },
|
||||
};
|
||||
|
||||
export const Loading: Story = {
|
||||
args: { loading: true, children: 'Saving…' },
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: { disabled: true, children: 'Not available' },
|
||||
};
|
||||
|
||||
export const FullWidth: Story = {
|
||||
args: { fullWidth: true, children: 'Full-width action' },
|
||||
parameters: {
|
||||
layout: 'padded',
|
||||
},
|
||||
};
|
||||
87
packages/ui/src/Button/Button.tsx
Normal file
87
packages/ui/src/Button/Button.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import type { ButtonHTMLAttributes, ReactNode } from 'react';
|
||||
import styles from './Button.module.css';
|
||||
|
||||
export type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger';
|
||||
export type ButtonSize = 'sm' | 'md' | 'lg';
|
||||
|
||||
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
/** Visual variant of the button. */
|
||||
variant?: ButtonVariant;
|
||||
/** Size of the button. */
|
||||
size?: ButtonSize;
|
||||
/** Renders a full-width block button. */
|
||||
fullWidth?: boolean;
|
||||
/** Shows a loading spinner and disables interaction. */
|
||||
loading?: boolean;
|
||||
/** Icon placed before the label. */
|
||||
leadingIcon?: ReactNode;
|
||||
/** Icon placed after the label. */
|
||||
trailingIcon?: ReactNode;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Core interactive button primitive.
|
||||
*
|
||||
* All interactive affordances follow WCAG 2.2 AA:
|
||||
* - Minimum 44 × 44 px touch target (sm/md/lg).
|
||||
* - Visible focus ring via `--focus-ring` token.
|
||||
* - `aria-disabled` and `aria-busy` set appropriately.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <Button variant="primary" onClick={handleSave}>Save changes</Button>
|
||||
* ```
|
||||
*/
|
||||
export function Button({
|
||||
variant = 'primary',
|
||||
size = 'md',
|
||||
fullWidth = false,
|
||||
loading = false,
|
||||
leadingIcon,
|
||||
trailingIcon,
|
||||
disabled,
|
||||
children,
|
||||
className,
|
||||
...rest
|
||||
}: ButtonProps) {
|
||||
const isDisabled = disabled || loading;
|
||||
|
||||
return (
|
||||
<button
|
||||
{...rest}
|
||||
disabled={isDisabled}
|
||||
aria-disabled={isDisabled}
|
||||
aria-busy={loading}
|
||||
className={[
|
||||
styles.button,
|
||||
styles[variant],
|
||||
styles[size],
|
||||
fullWidth ? styles.fullWidth : '',
|
||||
loading ? styles.loading : '',
|
||||
className ?? '',
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
{leadingIcon && (
|
||||
<span className={styles.icon} aria-hidden="true">
|
||||
{leadingIcon}
|
||||
</span>
|
||||
)}
|
||||
<span className={styles.label}>{children}</span>
|
||||
{trailingIcon && (
|
||||
<span className={styles.icon} aria-hidden="true">
|
||||
{trailingIcon}
|
||||
</span>
|
||||
)}
|
||||
{loading && (
|
||||
<span className={styles.spinner} role="status" aria-label="Loading">
|
||||
<span className={styles.spinnerDot} />
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default Button;
|
||||
44
packages/ui/src/Card/Card.module.css
Normal file
44
packages/ui/src/Card/Card.module.css
Normal file
@@ -0,0 +1,44 @@
|
||||
.card {
|
||||
background-color: var(--color-bg-surface);
|
||||
border: var(--border-width-default) solid var(--color-border-default);
|
||||
border-radius: var(--border-radius-lg);
|
||||
box-shadow: var(--shadow-sm);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: var(--space-4) var(--space-6);
|
||||
border-block-end: var(--border-width-default) solid var(--color-border-subtle);
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
font-size: var(--font-size-lg);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-text-primary);
|
||||
line-height: var(--line-height-tight);
|
||||
}
|
||||
|
||||
.description {
|
||||
margin: var(--space-1) 0 0;
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-text-muted);
|
||||
line-height: var(--line-height-normal);
|
||||
}
|
||||
|
||||
.body {
|
||||
padding: var(--space-6);
|
||||
}
|
||||
|
||||
.noPadding {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: var(--space-4) var(--space-6);
|
||||
border-block-start: var(--border-width-default) solid var(--color-border-subtle);
|
||||
background-color: var(--color-bg-subtle);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
65
packages/ui/src/Card/Card.stories.tsx
Normal file
65
packages/ui/src/Card/Card.stories.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { Button } from '../Button/Button';
|
||||
import { Card } from './Card';
|
||||
|
||||
const meta: Meta<typeof Card> = {
|
||||
title: 'Primitives/Card',
|
||||
component: Card,
|
||||
tags: ['autodocs'],
|
||||
parameters: {
|
||||
layout: 'padded',
|
||||
docs: {
|
||||
description: {
|
||||
component: 'Surface container for grouped content.',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Card>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
title: 'Unit 101',
|
||||
description: 'Sunrise Ridge HOA',
|
||||
children: 'Resident information and details would appear here.',
|
||||
},
|
||||
};
|
||||
|
||||
export const WithFooter: Story = {
|
||||
args: {
|
||||
title: 'Pending dues',
|
||||
description: 'Q1 2026 assessment',
|
||||
children: 'Outstanding balance: $350.00',
|
||||
footer: (
|
||||
<>
|
||||
<Button variant="primary" size="sm">
|
||||
Pay now
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm">
|
||||
View statement
|
||||
</Button>
|
||||
</>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
export const NoPadding: Story = {
|
||||
args: {
|
||||
title: 'Document list',
|
||||
noPadding: true,
|
||||
children: (
|
||||
<ul style={{ margin: 0, padding: '1rem 1.5rem', listStyle: 'none' }}>
|
||||
<li>HOA Bylaws 2025.pdf</li>
|
||||
<li>Meeting Minutes Q4 2025.pdf</li>
|
||||
</ul>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
export const ContentOnly: Story = {
|
||||
args: {
|
||||
children: 'A simple card with no title or footer.',
|
||||
},
|
||||
};
|
||||
60
packages/ui/src/Card/Card.tsx
Normal file
60
packages/ui/src/Card/Card.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import type { HTMLAttributes, ReactNode } from 'react';
|
||||
import styles from './Card.module.css';
|
||||
|
||||
export interface CardProps extends HTMLAttributes<HTMLElement> {
|
||||
/** Card heading text. */
|
||||
title?: string;
|
||||
/** Optional description or metadata shown below the title. */
|
||||
description?: string;
|
||||
/** Actions rendered in the card footer (e.g. buttons). */
|
||||
footer?: ReactNode;
|
||||
/** Removes the default padding from the content area. */
|
||||
noPadding?: boolean;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Surface container for grouped content.
|
||||
*
|
||||
* Uses a `<article>` element by default for semantic grouping.
|
||||
* Override with `as` if a different element is needed.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <Card title="Unit 101" description="Sunrise Ridge HOA">
|
||||
* <p>Resident details here.</p>
|
||||
* </Card>
|
||||
* ```
|
||||
*/
|
||||
export function Card({
|
||||
title,
|
||||
description,
|
||||
footer,
|
||||
noPadding = false,
|
||||
children,
|
||||
className,
|
||||
...rest
|
||||
}: CardProps) {
|
||||
return (
|
||||
<article {...rest} className={[styles.card, className ?? ''].filter(Boolean).join(' ')}>
|
||||
{(title || description) && (
|
||||
<header className={styles.header}>
|
||||
{title && <h3 className={styles.title}>{title}</h3>}
|
||||
{description && <p className={styles.description}>{description}</p>}
|
||||
</header>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={[styles.body, noPadding ? styles.noPadding : '']
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
{footer && <footer className={styles.footer}>{footer}</footer>}
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
export default Card;
|
||||
5
packages/ui/src/css.d.ts
vendored
Normal file
5
packages/ui/src/css.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/** TypeScript declarations for CSS Module files. */
|
||||
declare module '*.module.css' {
|
||||
const styles: Record<string, string>;
|
||||
export default styles;
|
||||
}
|
||||
5
packages/ui/src/index.ts
Normal file
5
packages/ui/src/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export { Button } from './Button/Button';
|
||||
export type { ButtonProps, ButtonVariant, ButtonSize } from './Button/Button';
|
||||
|
||||
export { Card } from './Card/Card';
|
||||
export type { CardProps } from './Card/Card';
|
||||
140
packages/ui/src/tokens/tokens.css
Normal file
140
packages/ui/src/tokens/tokens.css
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* Design token definitions for dwellops-platform.
|
||||
* All values are expressed as CSS custom properties.
|
||||
* Import this file once at the app root, then reference tokens throughout.
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* ── Color: Primitives ───────────────────────────────────────── */
|
||||
--color-neutral-0: #ffffff;
|
||||
--color-neutral-50: #f8f9fa;
|
||||
--color-neutral-100: #f1f3f5;
|
||||
--color-neutral-200: #e9ecef;
|
||||
--color-neutral-300: #dee2e6;
|
||||
--color-neutral-400: #ced4da;
|
||||
--color-neutral-500: #adb5bd;
|
||||
--color-neutral-600: #6c757d;
|
||||
--color-neutral-700: #495057;
|
||||
--color-neutral-800: #343a40;
|
||||
--color-neutral-900: #212529;
|
||||
--color-neutral-1000: #000000;
|
||||
|
||||
--color-brand-50: #e8f4fd;
|
||||
--color-brand-100: #bee3f8;
|
||||
--color-brand-200: #90cdf4;
|
||||
--color-brand-300: #63b3ed;
|
||||
--color-brand-400: #4299e1;
|
||||
--color-brand-500: #3182ce;
|
||||
--color-brand-600: #2b6cb0;
|
||||
--color-brand-700: #2c5282;
|
||||
--color-brand-800: #2a4365;
|
||||
--color-brand-900: #1a365d;
|
||||
|
||||
--color-success-50: #f0fdf4;
|
||||
--color-success-500: #22c55e;
|
||||
--color-success-700: #15803d;
|
||||
|
||||
--color-warning-50: #fffbeb;
|
||||
--color-warning-500: #f59e0b;
|
||||
--color-warning-700: #b45309;
|
||||
|
||||
--color-error-50: #fef2f2;
|
||||
--color-error-500: #ef4444;
|
||||
--color-error-700: #b91c1c;
|
||||
|
||||
/* ── Color: Semantic ─────────────────────────────────────────── */
|
||||
--color-bg-page: var(--color-neutral-50);
|
||||
--color-bg-surface: var(--color-neutral-0);
|
||||
--color-bg-subtle: var(--color-neutral-100);
|
||||
--color-bg-muted: var(--color-neutral-200);
|
||||
|
||||
--color-text-primary: var(--color-neutral-900);
|
||||
--color-text-secondary: var(--color-neutral-700);
|
||||
--color-text-muted: var(--color-neutral-600);
|
||||
--color-text-inverse: var(--color-neutral-0);
|
||||
|
||||
--color-border-default: var(--color-neutral-300);
|
||||
--color-border-subtle: var(--color-neutral-200);
|
||||
--color-border-strong: var(--color-neutral-500);
|
||||
|
||||
--color-interactive-default: var(--color-brand-600);
|
||||
--color-interactive-hover: var(--color-brand-700);
|
||||
--color-interactive-active: var(--color-brand-800);
|
||||
--color-interactive-focus: var(--color-brand-500);
|
||||
--color-interactive-disabled: var(--color-neutral-400);
|
||||
|
||||
/* ── Spacing ─────────────────────────────────────────────────── */
|
||||
--space-0: 0;
|
||||
--space-1: 0.25rem;
|
||||
--space-2: 0.5rem;
|
||||
--space-3: 0.75rem;
|
||||
--space-4: 1rem;
|
||||
--space-5: 1.25rem;
|
||||
--space-6: 1.5rem;
|
||||
--space-8: 2rem;
|
||||
--space-10: 2.5rem;
|
||||
--space-12: 3rem;
|
||||
--space-16: 4rem;
|
||||
--space-20: 5rem;
|
||||
--space-24: 6rem;
|
||||
|
||||
/* ── Typography ──────────────────────────────────────────────── */
|
||||
--font-family-base:
|
||||
system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
--font-family-mono: ui-monospace, 'Cascadia Code', 'Fira Code', monospace;
|
||||
|
||||
--font-size-xs: 0.75rem;
|
||||
--font-size-sm: 0.875rem;
|
||||
--font-size-base: 1rem;
|
||||
--font-size-lg: 1.125rem;
|
||||
--font-size-xl: 1.25rem;
|
||||
--font-size-2xl: 1.5rem;
|
||||
--font-size-3xl: 1.875rem;
|
||||
--font-size-4xl: 2.25rem;
|
||||
|
||||
--font-weight-normal: 400;
|
||||
--font-weight-medium: 500;
|
||||
--font-weight-semibold: 600;
|
||||
--font-weight-bold: 700;
|
||||
|
||||
--line-height-tight: 1.25;
|
||||
--line-height-snug: 1.375;
|
||||
--line-height-normal: 1.5;
|
||||
--line-height-relaxed: 1.625;
|
||||
|
||||
/* ── Border ──────────────────────────────────────────────────── */
|
||||
--border-width-default: 1px;
|
||||
--border-width-medium: 2px;
|
||||
|
||||
--border-radius-sm: 0.25rem;
|
||||
--border-radius-base: 0.375rem;
|
||||
--border-radius-md: 0.5rem;
|
||||
--border-radius-lg: 0.75rem;
|
||||
--border-radius-xl: 1rem;
|
||||
--border-radius-full: 9999px;
|
||||
|
||||
/* ── Shadow ──────────────────────────────────────────────────── */
|
||||
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--shadow-base: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
|
||||
/* ── Transition ──────────────────────────────────────────────── */
|
||||
--transition-fast: 100ms ease-in-out;
|
||||
--transition-base: 200ms ease-in-out;
|
||||
--transition-slow: 300ms ease-in-out;
|
||||
|
||||
/* ── Focus ring ──────────────────────────────────────────────── */
|
||||
--focus-ring: 0 0 0 3px var(--color-brand-300);
|
||||
|
||||
/* ── Z-index ─────────────────────────────────────────────────── */
|
||||
--z-below: -1;
|
||||
--z-base: 0;
|
||||
--z-raised: 10;
|
||||
--z-dropdown: 100;
|
||||
--z-sticky: 200;
|
||||
--z-overlay: 300;
|
||||
--z-modal: 400;
|
||||
--z-toast: 500;
|
||||
--z-tooltip: 600;
|
||||
}
|
||||
9
packages/ui/tsconfig.json
Normal file
9
packages/ui/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "@dwellops/config/tsconfig/react-library.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
1
packages/ui/tsconfig.tsbuildinfo
Normal file
1
packages/ui/tsconfig.tsbuildinfo
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user