diff --git a/exec/cli.js b/exec/cli.js index 92ed266..37ab204 100755 --- a/exec/cli.js +++ b/exec/cli.js @@ -18,7 +18,7 @@ Initialization Summary:`),a.info(` Components Dir: ${f.bold(N)}`),a.info(` CSS ${r.name} (${i}) `),a.info(`Status: ${d?f.green("installed"):f.dim("available")}`),a.info(`Install: ${f.bold(b.installCommand)}`),a.info(`Exports: ${g.join(", ")||r.name}`),a.info(`NPM Dependencies: ${b.dependencies.join(", ")||"none"}`),a.info(`Registry Dependencies: ${b.registryDependencies.join(", ")||"none"}`),x.length>0){a.bold(` Props Definitions:`);for(let k of x)console.log(` -${k}`)}console.log()}async function ge(e="AGENTS.md",t){let s=await I(),i=T(s),r=t??await $(),p=S((r==null?void 0:r.componentsDir)??i.components)||i.components,o=S((r==null?void 0:r.cssDir)??i.css),l=o?`${o}/dashkit.css`:"dashkit.css",c=u.resolve(process.cwd(),e),n=Object.keys(w).sort((m,g)=>m.localeCompare(g)),d=["","## Dashkit UI \u2014 AI Assistant Guidelines","",`- **Components Directory**: \`${p}\` (import from \`${p}/\` or barrel \`${p}\`)`,`- **Theme & Tokens File**: \`${l}\``,"- **Install Components**: `bunx carllosnc/dashkit add `","- **Inspect Component Props**: `bunx carllosnc/dashkit info --json`",`- **Available Components (${n.length})**: ${n.map(m=>`\`${m}\``).join(", ")}`,"","### 3-Tier AI Customization Protocol",`1. **Global Theme (\`${l}\`)**: Edit \`:root\` / \`.dark\` variables (\`--ds-primary\`, \`--ds-bg\`, \`--ds-card\`, \`--radius\`, \`--ds-primary-gradient\`) or set \`data-primary\` / \`data-base\` attributes. Never scatter inline theme classes across pages.`,`2. **Component Variants (\`${p}//.tsx\`)**: Edit \`cva(...)\` variants directly inside the component file when changing a component across the app.`,'3. **Instance Overrides (`className` & `data-slot`)**: Use `className` (merged via `cn()`) or `data-slot` selectors (`className="[&_[data-slot=label]]:uppercase"`) for one-off tweaks.',"","### Component API Patterns","- **Compound (`children`)**: `Card` (`CardHeader`, `CardContent`, `CardFooter`), `Tabs` (`TabsList`, `TabsTrigger`, `TabsContent`), `Accordion` (`AccordionItem`, `AccordionTrigger`, `AccordionContent`), `Modal` (`ModalHeader`, `ModalContent`, `ModalFooter`), `Drawer` (`DrawerHeader`, `DrawerContent`, `DrawerFooter`), `Dropdown`, `Popover`, `Table`, `Navbar`, `Breadcrumb`, `Stepper`.","- **Prop-Driven (`options` / props)**: `Select` & `Combobox` use `options={[{ value, label }]}`; `Input`, `Textarea`, `DatePicker`, `DateField`, `ColorPicker`, `CopyField`, `OtpInput`, `Checkbox`, `Radio`, `Switch`, `Slider` take `label`, `description`/`helperText`, `error` as props; `StatsCard` takes `title`, `value`, `trend`, `status`, `chart`.",""].join(` +${k}`)}console.log()}async function ge(e="AGENTS.md",t){let s=await I(),i=T(s),r=t??await $(),p=S((r==null?void 0:r.componentsDir)??i.components)||i.components,o=S((r==null?void 0:r.cssDir)??i.css),l=o?`${o}/dashkit.css`:"dashkit.css",c=u.resolve(process.cwd(),e),n=Object.keys(w).sort((m,g)=>m.localeCompare(g)),d=["","## Dashkit UI \u2014 AI Assistant Guidelines","",`- **Components Directory**: \`${p}\` (import from \`${p}/\` or barrel \`${p}\`)`,`- **Theme & Tokens File**: \`${l}\``,"- **Install Components**: `bunx carllosnc/dashkit add `","- **Inspect Component Props**: `bunx carllosnc/dashkit info --json`",`- **Available Components (${n.length})**: ${n.map(m=>`\`${m}\``).join(", ")}`,"","### 3-Tier AI Customization Protocol",`1. **Global Theme (\`${l}\`)**: Edit \`:root\` / \`.dark\` variables (\`--ds-primary\`, \`--ds-bg\`, \`--ds-card\`, \`--radius\`, \`--ds-pill-radius\`, \`--ds-border-width\`, \`--ds-button-border\`, \`--ds-button-shadow\`, \`--ds-card-shadow\`, \`--ds-input-shadow\`, \`--ds-primary-gradient\`) or set \`data-primary\` / \`data-base\` attributes. Never scatter inline theme classes across pages.`,`2. **Component Variants (\`${p}//.tsx\`)**: Edit \`cva(...)\` variants directly inside the component file when changing a component across the app.`,'3. **Instance Overrides (`className` & `data-slot`)**: Use `className` (merged via `cn()`) or `data-slot` selectors (`className="[&_[data-slot=label]]:uppercase"`) for one-off tweaks.',"","### Component API Patterns","- **Compound (`children`)**: `Card` (`CardHeader`, `CardContent`, `CardFooter`), `Tabs` (`TabsList`, `TabsTrigger`, `TabsContent`), `Accordion` (`AccordionItem`, `AccordionTrigger`, `AccordionContent`), `Modal` (`ModalHeader`, `ModalContent`, `ModalFooter`), `Drawer` (`DrawerHeader`, `DrawerContent`, `DrawerFooter`), `Dropdown`, `Popover`, `Table`, `Navbar`, `Breadcrumb`, `Stepper`.","- **Prop-Driven (`options` / props)**: `Select` & `Combobox` use `options={[{ value, label }]}`; `Input`, `Textarea`, `DatePicker`, `DateField`, `ColorPicker`, `CopyField`, `OtpInput`, `Checkbox`, `Radio`, `Switch`, `Slider` take `label`, `description`/`helperText`, `error` as props; `StatsCard` takes `title`, `value`, `trend`, `status`, `chart`.",""].join(` `);if(await h.pathExists(c)){let m=await h.readFile(c,"utf8"),g=/[\s\S]*?/,x=g.test(m)?m.replace(g,d):`${m.trimEnd()} ${d} diff --git a/public/llms-full.txt b/public/llms-full.txt index ee1b069..f0b5de9 100644 --- a/public/llms-full.txt +++ b/public/llms-full.txt @@ -202,6 +202,7 @@ import { BentoGrid, BentoGridItem } from '@/dashkit/BentoGrid'; By default, `BentoGrid` uses `gap="none"` to render a continuous `1px` hairline divider between cells without double borders—even when combining multi-row vertical spans (`rowSpan={2}`) and full-width spans (`colSpan="full"`). + 01 / Ingestion @@ -259,6 +260,7 @@ By default, `BentoGrid` uses `gap="none"` to render a continuous `1px` hairline + ```tsx Cell 1 @@ -279,6 +281,7 @@ By default, `BentoGrid` uses `gap="none"` to render a continuous `1px` hairline Set `rounded="none"` for a sharp editorial or architectural blueprint layout where every outer corner and inner intersection meets at 90 degrees. + col-span-4 @@ -300,6 +303,7 @@ Set `rounded="none"` for a sharp editorial or architectural blueprint layout whe + ```tsx col-span-4 @@ -317,6 +321,7 @@ Set `rounded="none"` for a sharp editorial or architectural blueprint layout whe Pass `gap="sm" | "md" | "lg"` when you want detached Bento cards instead of shared hairline borders. + Detached Bento Card (2 cols) @@ -328,6 +333,7 @@ Pass `gap="sm" | "md" | "lg"` when you want detached Bento cards instead of shar + ```tsx Detached Bento Card @@ -337,6 +343,7 @@ Pass `gap="sm" | "md" | "lg"` when you want detached Bento cards instead of shar ### BentoGrid Props + | Property | Type | Default | Description | | :--- | :--- | :--- | :--- | | `cols` | `BentoResponsiveValue` | `3` | Number of grid columns. Accepts a direct number (stacks on mobile, applies at md+) or a responsive breakpoint object ({ base, sm, md, lg, xl }). | @@ -345,8 +352,10 @@ Pass `gap="sm" | "md" | "lg"` when you want detached Bento cards instead of shar | `rounded` | `'none' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | `'xl'` | Controls the outer corner radius of the grid container. | | `autoRows` | `string` | - | Sets the CSS grid-auto-rows property (e.g., 'minmax(140px, auto)'). | + ### BentoGridItem Props + | Property | Type | Default | Description | | :--- | :--- | :--- | :--- | | `colSpan` | `BentoResponsiveValue<1..12 \| 'full'>` | `1` | Number of columns the cell spans. Accepts a direct value or a responsive breakpoint object. | @@ -1566,6 +1575,60 @@ The component is highly accessible and supports a fluid layout for its triggers. ``` +### Pills Variant (Segmented Control) + +Use `variant="pills"` to render tabs inside a compact, segmented track with an elevated floating indicator that smoothly slides between options. + + + + + Overview + Analytics + Reports + Notifications + + + + Platform Overview + General metric summary across all edge environments. + + + + + Live Analytics + Real-time latency, request rate, and error rate telemetry. + + + + + Generated Reports + Weekly and monthly compliance export records. + + + + + Notification Feeds + System warnings and deployment completion triggers. + + + + + +```tsx + + + Overview + Analytics + Reports + Notifications + + Overview metrics + Analytics telemetry + Generated reports + Notification feeds + +``` + ### Icons & Advanced Previews You can easily integrate icons and complex child layouts within your tabs. @@ -1649,6 +1712,7 @@ The indicator uses Framer Motion's `layoutId` to achieve a "sliding" effect when | `defaultValue` | `string` | - | The value of the tab that should be active on mount. | | `value` | `string` | - | The controlled value of the active tab. | | `onValueChange` | `(val: string) => void` | - | Callback fired when the active tab changes. | +| `variant` | `'line' \| 'pills'` | `'line'` | Visual styling variant: 'line' for standard bottom-bordered tabs, 'pills' for segmented control. | | `children` | `ReactNode` | - | Content of the tabs container. | | `className` | `string` | - | Additional CSS classes for the container. | diff --git a/public/llms.txt b/public/llms.txt index d3cc0d8..e0a7ddb 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -47,6 +47,10 @@ Dashkit uses a two-layer token system in `dashkit.css`: `@theme inline` maps sem - `--ds-danger`, `--ds-success`, `--ds-warning`, `--ds-info` (plus `*-fg` pairs): Semantic feedback states. - `--ds-chart-1` through `--ds-chart-5`: Categorical data visualization palette. - `--radius`: Master border radius (`0.75rem` default). `--radius-sm`, `--radius-md`, `--radius-lg`, `--radius-xl`, and `--radius-2xl` scale proportionally from `--radius` (set `--radius: 0rem` for sharp corners). +- `--ds-pill-radius`: Pill radius for `Button`, `Switch`, `Chip`, `Slider`, and `ProgressBar` (`calc(var(--radius) * 9999)` default; automatically becomes `0rem` when `--radius: 0rem`, or set explicitly e.g. `--ds-pill-radius: var(--radius-md)`). +- `--ds-border-width`: Structural border width for Cards, Buttons, Chips, and Inputs (`1px` default; set to `2px` for Neo-Brutalism). +- `--ds-button-border`: Border color for filled and soft Buttons (`transparent` default; set to `var(--ds-border)` or `var(--ds-primary)` for bordered buttons). +- `--ds-button-shadow`, `--ds-button-outlined-shadow`, `--ds-button-soft-shadow`, `--ds-card-shadow`, `--ds-input-shadow`: Elevation or hard Neo-Brutalist offset shadows (e.g. `3px 3px 0px 0px var(--ds-border)`) or CRT glow shadows. - `--ds-primary-gradient` / `--ds-danger-gradient`: Optional gradient overrides (set to `none` for flat buttons). ### Dynamic Runtime Theme Attributes diff --git a/public/registry.json b/public/registry.json index cf24298..e1e6564 100644 --- a/public/registry.json +++ b/public/registry.json @@ -209,8 +209,8 @@ "BentoGridItem" ], "propsDefinitions": [ - "export interface BentoGridProps\n extends React.HTMLAttributes,\n VariantProps {\n cols?: BentoResponsiveValue;\n autoRows?: string;\n}", - "export interface BentoGridItemProps\n extends React.HTMLAttributes,\n Omit, 'divided'> {\n colSpan?: BentoResponsiveValue;\n rowSpan?: BentoResponsiveValue;\n}" + "export interface BentoGridProps\r\n extends React.HTMLAttributes,\r\n VariantProps {\r\n cols?: BentoResponsiveValue;\r\n autoRows?: string;\r\n}", + "export interface BentoGridItemProps\r\n extends React.HTMLAttributes,\r\n Omit, 'divided'> {\r\n colSpan?: BentoResponsiveValue;\r\n rowSpan?: BentoResponsiveValue;\r\n}" ] }, "breadcrumb": { diff --git a/scripts/commands.ts b/scripts/commands.ts index d29f097..10d4084 100644 --- a/scripts/commands.ts +++ b/scripts/commands.ts @@ -800,7 +800,7 @@ async function writeAiRulesFile( `- **Available Components (${allSlugs.length})**: ${allSlugs.map((s) => `\`${s}\``).join(', ')}`, '', '### 3-Tier AI Customization Protocol', - `1. **Global Theme (\`${cssFile}\`)**: Edit \`:root\` / \`.dark\` variables (\`--ds-primary\`, \`--ds-bg\`, \`--ds-card\`, \`--radius\`, \`--ds-primary-gradient\`) or set \`data-primary\` / \`data-base\` attributes. Never scatter inline theme classes across pages.`, + `1. **Global Theme (\`${cssFile}\`)**: Edit \`:root\` / \`.dark\` variables (\`--ds-primary\`, \`--ds-bg\`, \`--ds-card\`, \`--radius\`, \`--ds-pill-radius\`, \`--ds-border-width\`, \`--ds-button-border\`, \`--ds-button-shadow\`, \`--ds-card-shadow\`, \`--ds-input-shadow\`, \`--ds-primary-gradient\`) or set \`data-primary\` / \`data-base\` attributes. Never scatter inline theme classes across pages.`, `2. **Component Variants (\`${componentsDir}//.tsx\`)**: Edit \`cva(...)\` variants directly inside the component file when changing a component across the app.`, '3. **Instance Overrides (`className` & `data-slot`)**: Use `className` (merged via `cn()`) or `data-slot` selectors (`className="[&_[data-slot=label]]:uppercase"`) for one-off tweaks.', '', diff --git a/scripts/generate-llms.ts b/scripts/generate-llms.ts index ac959ed..27cd47f 100644 --- a/scripts/generate-llms.ts +++ b/scripts/generate-llms.ts @@ -257,6 +257,10 @@ export function generateLlmsTxt(): string { lines.push('- `--ds-danger`, `--ds-success`, `--ds-warning`, `--ds-info` (plus `*-fg` pairs): Semantic feedback states.'); lines.push('- `--ds-chart-1` through `--ds-chart-5`: Categorical data visualization palette.'); lines.push('- `--radius`: Master border radius (`0.75rem` default). `--radius-sm`, `--radius-md`, `--radius-lg`, `--radius-xl`, and `--radius-2xl` scale proportionally from `--radius` (set `--radius: 0rem` for sharp corners).'); + lines.push('- `--ds-pill-radius`: Pill radius for `Button`, `Switch`, `Chip`, `Slider`, and `ProgressBar` (`calc(var(--radius) * 9999)` default; automatically becomes `0rem` when `--radius: 0rem`, or set explicitly e.g. `--ds-pill-radius: var(--radius-md)`).'); + lines.push('- `--ds-border-width`: Structural border width for Cards, Buttons, Chips, and Inputs (`1px` default; set to `2px` for Neo-Brutalism).'); + lines.push('- `--ds-button-border`: Border color for filled and soft Buttons (`transparent` default; set to `var(--ds-border)` or `var(--ds-primary)` for bordered buttons).'); + lines.push('- `--ds-button-shadow`, `--ds-button-outlined-shadow`, `--ds-button-soft-shadow`, `--ds-card-shadow`, `--ds-input-shadow`: Elevation or hard Neo-Brutalist offset shadows (e.g. `3px 3px 0px 0px var(--ds-border)`) or CRT glow shadows.'); lines.push('- `--ds-primary-gradient` / `--ds-danger-gradient`: Optional gradient overrides (set to `none` for flat buttons).\n'); lines.push('### Dynamic Runtime Theme Attributes'); diff --git a/scripts/tests/registry.test.ts b/scripts/tests/registry.test.ts index 7155b0b..721e323 100644 --- a/scripts/tests/registry.test.ts +++ b/scripts/tests/registry.test.ts @@ -19,10 +19,10 @@ describe('Registry Integrity', () => { }); it('should ensure all files in component folders are registered in registry.ts', () => { - // Get all subdirectories in src/dashkit, except 'utils' + // Get all subdirectories in src/dashkit, except 'utils' and 'presets' const subdirs = fs.readdirSync(componentsDir).filter((file) => { const fullPath = path.join(componentsDir, file); - return fs.statSync(fullPath).isDirectory() && file !== 'utils'; + return fs.statSync(fullPath).isDirectory() && file !== 'utils' && file !== 'presets'; }); // Collect all registered files across all components, normalized to forward slashes diff --git a/src/dashkit/Avatar/AvatarGroup.tsx b/src/dashkit/Avatar/AvatarGroup.tsx index 7606693..e6b9946 100644 --- a/src/dashkit/Avatar/AvatarGroup.tsx +++ b/src/dashkit/Avatar/AvatarGroup.tsx @@ -42,7 +42,7 @@ export function AvatarGroup({ children, max, size = 'md', spacing = 'md', classN )} diff --git a/src/dashkit/Badge/Badge.tsx b/src/dashkit/Badge/Badge.tsx index 0ef61b7..a1e6128 100644 --- a/src/dashkit/Badge/Badge.tsx +++ b/src/dashkit/Badge/Badge.tsx @@ -27,7 +27,7 @@ const badgeVariants = cva( }, compoundVariants: [ // Base - { variant: 'solid', color: 'base', className: 'bg-ds-500 text-white' }, + { variant: 'solid', color: 'base', className: 'bg-ds-600 text-ds-50 dark:bg-ds-500 dark:text-ds-950' }, { variant: 'soft', color: 'base', className: 'bg-ds-500/15 text-ds-600 dark:text-ds-400 dark:bg-ds-500/20' }, // Success { variant: 'solid', color: 'success', className: 'bg-ds-success-600 text-ds-success-50 dark:bg-ds-success-500 dark:text-ds-success-950' }, diff --git a/src/dashkit/Breadcrumb/BreadcrumbItem.tsx b/src/dashkit/Breadcrumb/BreadcrumbItem.tsx index 6145d4b..7266efe 100644 --- a/src/dashkit/Breadcrumb/BreadcrumbItem.tsx +++ b/src/dashkit/Breadcrumb/BreadcrumbItem.tsx @@ -20,8 +20,8 @@ export const BreadcrumbItem = React.forwardRef {children} diff --git a/src/dashkit/Button/Button.tsx b/src/dashkit/Button/Button.tsx index bc1be5c..4762ff4 100644 --- a/src/dashkit/Button/Button.tsx +++ b/src/dashkit/Button/Button.tsx @@ -16,9 +16,9 @@ const buttonVariants = cva( { variants: { variant: { - filled: '', - outlined: 'bg-card text-foreground border', - soft: '', + filled: 'border ds-button-surface', + outlined: 'bg-card text-foreground border ds-button-outlined-surface', + soft: 'border ds-button-soft-surface', ghost: 'bg-transparent border border-transparent', }, color: { @@ -31,7 +31,7 @@ const buttonVariants = cva( lg: 'h-11 text-base', }, rounded: { - true: 'rounded-full', + true: 'rounded-full ds-pill', false: 'rounded-md', }, }, @@ -40,7 +40,7 @@ const buttonVariants = cva( { variant: 'filled', color: 'primary', - className: 'ds-primary-gradient text-primary-foreground shadow-xs hover:brightness-105', + className: 'ds-primary-gradient text-primary-foreground hover:brightness-105', }, { variant: 'filled', @@ -51,7 +51,7 @@ const buttonVariants = cva( { variant: 'outlined', color: 'primary', - className: 'border-ds-300 hover:bg-ds-50 dark:border-ds-700 dark:hover:bg-ds-800/50 dark:hover:brightness-110', + className: 'border-border hover:bg-muted/60 dark:hover:bg-muted/60 dark:hover:brightness-110', }, { variant: 'outlined', diff --git a/src/dashkit/ButtonGroup/ButtonGroup.tsx b/src/dashkit/ButtonGroup/ButtonGroup.tsx index 453128e..1c97cb7 100644 --- a/src/dashkit/ButtonGroup/ButtonGroup.tsx +++ b/src/dashkit/ButtonGroup/ButtonGroup.tsx @@ -45,9 +45,8 @@ export function ButtonGroupItem({ - -``` - -#### Available Base Palettes (`data-base`) -- `zinc` (default), `slate`, `gray`, `neutral`, `stone`, `taupe`, `mauve`, `mist`, `olive` - -#### Available Primary Palettes (`data-primary`) -- `blue` (default), `violet`, `indigo`, `emerald`, `green`, `rose`, `orange`, `amber`, `cyan`, `teal`, `fuchsia`, `red`, `yellow`, `lime`, `sky`, `purple`, `pink` - -#### OLED Dark Mode (`data-dark-bg`) -- `oled` (switches `.dark` background `--ds-bg` to pure `#000000` black) - -### Static Token Customization - -To customize Dashkit statically, edit the variables in `:root` and `.dark` inside `src/dashkit/dashkit.css`. - -### Example: Customizing Roundness - -Because `--radius-sm` through `--radius-2xl` are computed proportionally from `--radius`, changing `--radius` in `:root` updates the entire system: - -```css -:root { - --radius: 1rem; /* Soft, rounded geometry (use 0rem for sharp corners) */ -} -``` - -### Example: Custom Brand Color - -To change the primary color to a custom purple: +Dashkit connects semantic utility classes to CSS custom properties via `@theme inline` in `src/dashkit/dashkit.css`: ```css -:root { - --ds-primary: #7c3aed; /* Violet 600 */ - --ds-primary-fg: #ffffff; -} - -.dark { - --ds-primary: #a78bfa; /* Violet 400 */ +@theme inline { + --color-background: var(--ds-bg); + --color-foreground: var(--ds-fg); + --color-card: var(--ds-card); + --color-popover: var(--ds-popover); + --color-border: var(--ds-border); + --color-primary: var(--ds-primary); + --color-primary-foreground: var(--ds-primary-fg); + --color-input-bg: var(--ds-input-bg); + --color-input-border: var(--ds-input-border); + --color-input-focus: var(--ds-input-focus); + --color-input-fg: var(--ds-input-fg); } ``` -## Creating Your Own Tokens - -When building your own components, you can add new tokens to the `:root` block to keep your design consistent: - -1. **Define the token** in `dashkit.css`: - ```css - :root { - --ds-special-bg: var(--color-ds-primary-500); - } - ``` -2. **Map it** in `@theme inline`: - ```css - @theme inline { - --color-special-bg: var(--ds-special-bg); - } - ``` -3. **Use it** in your component: - ```tsx -
- My custom themed component -
- ``` - -## AI-First Customization Protocol - -Dashkit is architected for **AI-driven customization via prompts**. Because you own the source code in `src/dashkit/`, AI agents should follow a strict 3-tier hierarchy instead of scattering inline classes across pages: - -1. **Tier 1 — Global Theme (`src/dashkit/dashkit.css`)**: Change `--ds-primary`, `--ds-bg`, `--ds-card`, `--radius`, or set `--ds-primary-gradient: none` for flat buttons. -2. **Tier 2 — Component Variants (`src/dashkit/[Component]/[Component].tsx`)**: Edit `cva(...)` definitions directly in the component file when changing a component's default look or adding a new variant across the app. -3. **Tier 3 — One-Off Overrides (`className` & `data-slot`)**: Use `className` for single-instance tweaks (the `cn` utility automatically resolves conflicts between standard Tailwind classes like `rounded-full` / `bg-none` and Dashkit's `ds-rounded*` / `ds-*-gradient`). For sub-elements inside form components (`Input`, `Select`, `Combobox`, `DatePicker`, etc.), target `data-slot` attributes (e.g., `className="[&_[data-slot=label]]:uppercase [&_[data-slot=trigger]]:rounded-full"`). - -### AI Prompt Example - -Copy and paste this prompt to quickly generate a unique theme: - -> **AI Prompt:** -> "I want to create a new theme for Dashkit UI. The theme should use a professional 'Emerald and Slate' palette. Please update `dashkit.css` by: -> 1. Updating the `:root` and `.dark` variables for bg, fg, primary, and secondary. -> 2. Setting `--radius: 0.5rem` for a sharper, denser geometric profile. -> 3. Ensuring inputs use a clean, bordered style with subtle rings. -> Keep all page components pure without inline style overrides." - - - +--- + +## Token Reference + +### Core Surfaces & Borders +- `--ds-bg`: Base canvas background (`bg-background`) +- `--ds-fg`: Main foreground text (`text-foreground`) +- `--ds-card`: Card and panel surface (`bg-card`) +- `--ds-card-fg`: Card text foreground (`text-card-foreground`) +- `--ds-popover`: Floating surfaces for dropdowns, tooltips, dialogs (`bg-popover`) +- `--ds-border`: Default border color (`border-border`) + +### Interactive & Brand Actions +- `--ds-primary`: Primary action color (`bg-primary`, `text-primary`) +- `--ds-primary-fg`: Text on primary action (`text-primary-foreground`) +- `--ds-primary-gradient`: Linear gradient for filled buttons (`none` for flat style) +- `--ds-secondary`: Secondary action background (`bg-secondary`) +- `--ds-accent`: Subtle selection and hover highlight (`bg-accent`) + +### Status & Feedback +- `--ds-danger` / `--ds-danger-fg`: Error and destructive actions (`text-danger`, `bg-danger`) +- `--ds-success` / `--ds-success-fg`: Confirmation and positive indicators (`text-success`) +- `--ds-warning` / `--ds-warning-fg`: Caution and warning alerts (`text-warning`) +- `--ds-info` / `--ds-info-fg`: Informational badges and icons (`text-info`) + +### Form & Input Controls +- `--ds-input-bg`: Input field background (`bg-input-bg`) +- `--ds-input-border`: Unfocused input border (`border-input-border`) +- `--ds-input-focus`: Focused ring and border highlight (`focus:border-input-focus`) +- `--ds-input-fg`: Input value text color (`text-input-fg`) +- `--ds-input-ring`: Focus ring box-shadow color + +### Geometry, Corner Radius & Tactile Shadows +All corner radii derive dynamically from `--radius`. Modifying `--radius` updates all components instantly without React changes: +- `--radius`: Master geometric scale (Default: `0.75rem`) +- `--radius-sm`: Derived as `max(0px, calc(var(--radius) - 5px))` (Checkboxes, small Badges) +- `--radius-md`: Derived as `max(0px, calc(var(--radius) - 3px))` (Inputs, Buttons) +- `--radius-lg`: Derived as `var(--radius)` (Cards, Popovers) +- `--radius-xl`: Derived as `calc(var(--radius) * 1.3333)` (Modals, Drawers) +- `--radius-2xl`: Derived as `calc(var(--radius) * 1.6667)` (Hero containers) +- `--ds-pill-radius`: Derived as `calc(var(--radius) * 9999)` (Pill Buttons, Switches, Chips, Sliders; set to `0rem` for square pills) +- `--ds-border-width`: Structural border thickness for surfaces and controls (Default: `1px`, `2px` in Brutalism) +- `--ds-card-shadow`: Surface elevation, hard offset, or glow shadow (Default: `none`) +- `--ds-button-shadow`: Tactile elevation on buttons (Default: `0 1px 2px 0 rgb(0 0 0 / 0.05)`) +- `--ds-button-border`: Border color for filled/soft buttons (Default: `transparent`, `var(--ds-border)` in Brutalism) + +--- + +## Dynamic Runtime Attributes + +Dashkit supports instant dynamic switching via HTML attributes without changing CSS files: + +### Base Neutral Palette (`data-base`) +Applies neutral gray scales (`ds-0` through `ds-950`): +- Values: `zinc` (default), `slate`, `gray`, `neutral`, `stone`, `taupe`, `mauve`, `mist`, `olive` +- Scope: Set on `` or scoped on any container `
` + +### Primary Brand Color (`data-primary`) +Applies primary brand swatches (`ds-primary-50` through `ds-primary-950`): +- Values: `blue` (default), `violet`, `indigo`, `emerald`, `green`, `rose`, `orange`, `amber`, `cyan`, `teal`, `fuchsia`, `red`, `yellow`, `lime`, `sky`, `purple`, `pink` +- Scope: Set on `` or scoped on any container `
` + +### Dark & OLED Background (`data-dark-bg`) +- `data-dark-bg="oled"`: Forces `.dark` background `--ds-bg` to pure `#000000` black. + +--- + +## Preset Delta Layer (`src/dashkit/presets/`) + +Presets are token-override layers scoped to `[data-preset=""]`: + +- `src/dashkit/dashkit.css`: Base engine and default token values. +- `src/dashkit/presets/brutalist.css`: Neo-Brutalist preset (`--radius: 0rem`, `--ds-pill-radius: 0rem`, `--ds-border-width: 2px`, hard offset shadows). +- `src/dashkit/presets/matrix.css`: Cyber Terminal preset (`--radius: 0.125rem`, phosphor green palette, CRT glow shadows, monospace font). +- `src/dashkit/presets/index.css`: Preset aggregator. diff --git a/src/pages/tabs.mdx b/src/pages/tabs.mdx index f17afab..8703efb 100644 --- a/src/pages/tabs.mdx +++ b/src/pages/tabs.mdx @@ -52,6 +52,60 @@ The component is highly accessible and supports a fluid layout for its triggers. ``` +## Pills Variant (Segmented Control) + +Use `variant="pills"` to render tabs inside a compact, segmented track with an elevated floating indicator that smoothly slides between options. + +
+ + + Overview + Analytics + Reports + Notifications + + +
+

Platform Overview

+

General metric summary across all edge environments.

+
+
+ +
+

Live Analytics

+

Real-time latency, request rate, and error rate telemetry.

+
+
+ +
+

Generated Reports

+

Weekly and monthly compliance export records.

+
+
+ +
+

Notification Feeds

+

System warnings and deployment completion triggers.

+
+
+
+
+ +```tsx + + + Overview + Analytics + Reports + Notifications + + Overview metrics + Analytics telemetry + Generated reports + Notification feeds + +``` + ## Icons & Advanced Previews You can easily integrate icons and complex child layouts within your tabs. @@ -149,6 +203,12 @@ The indicator uses Framer Motion's `layoutId` to achieve a "sliding" effect when "defaultValue": "—", "description": "Callback fired when the active tab changes." }, + { + "prop": "variant", + "type": "'line' | 'pills'", + "defaultValue": "'line'", + "description": "Visual styling variant: 'line' for standard bottom-bordered tabs, 'pills' for segmented control." + }, { "prop": "children", "type": "ReactNode", diff --git a/src/partials/CustomizationPlayground.tsx b/src/partials/CustomizationPlayground.tsx new file mode 100644 index 0000000..5a27389 --- /dev/null +++ b/src/partials/CustomizationPlayground.tsx @@ -0,0 +1,333 @@ +import { useState } from 'react'; +import { useTheme } from './ThemeContext'; +import { + THEME_PRESETS, + BASE_COLORS, + PRIMARY_COLORS, + type BaseColor, + type PrimaryColor, +} from './themeConfig'; +import { + Card, + CardHeader, + CardTitle, + CardDescription, + CardContent, +} from '../dashkit/Card'; +import { Badge } from '../dashkit/Badge'; +import { Button } from '../dashkit/Button/Button'; +import { + Table, + TableHeader, + TableBody, + TableRow, + TableHead, + TableCell, +} from '../dashkit/Table/Table'; +import { FiCheck, FiCopy, FiRotateCcw, FiSun, FiMoon } from 'react-icons/fi'; + +export function CustomizationPlayground() { + const { + theme, + toggleTheme, + baseColor, + setBaseColor, + primaryColor, + setPrimaryColor, + preset, + setPreset, + } = useTheme(); + + const [copiedCss, setCopiedCss] = useState(false); + + const activePreset = + THEME_PRESETS.find(p => p.id === preset) ?? THEME_PRESETS[0]; + + const handleCopyCss = async () => { + try { + await navigator.clipboard.writeText(activePreset.cssSnippet); + setCopiedCss(true); + setTimeout(() => setCopiedCss(false), 2000); + } catch (err) { + console.error('Failed to copy CSS snippet:', err); + } + }; + + return ( +
+ {/* 1. Preset Switcher Card */} + + +
+
+ Interactive Theme Presets + + Select a preset below to transform the entire documentation site and inspect its live CSS tokens. + +
+
+ + +
+
+
+ + + {/* Preset Option Cards (Stacked List) */} +
+ {THEME_PRESETS.map(item => { + const isSelected = preset === item.id; + return ( + + ); + })} +
+ + {/* Palette Fine-Tuning */} +
+
+ + Primary Brand Palette (`data-primary="{primaryColor}"`) + +
+ {PRIMARY_COLORS.map(c => { + const isSelected = primaryColor === c.id; + return ( + + ); + })} +
+
+ +
+ + Base Neutral Palette (`data-base="{baseColor}"`) + +
+ {BASE_COLORS.map(c => { + const isSelected = baseColor === c.id; + return ( + + ); + })} +
+
+
+
+
+ + {/* 2. Dynamic Core Variables Table */} + + +
+
+ + Active Design Tokens — {activePreset.label} Preset + + + Real-time CSS custom properties currently powering the interface in Light and Dark modes. + +
+ +
+
+ + + + + Token Variable + Category + Light Value (`:root`) + Dark Value (`.dark`) + Role + + + + {activePreset.tokens.map(token => ( + + + {token.variable} + + + + + + + {token.lightPreviewColor && ( + + )} + {token.lightValue} + + + + + {token.darkPreviewColor && ( + + )} + {token.darkValue} + + + + {token.description} + + + ))} + +
+
+
+ + {/* 3. Dynamic Radius Scale & Generated CSS */} +
+ + + Proportional Radius Scale + + All component corner tokens derive automatically from{' '} + + --radius: {activePreset.radius} + + . + + + + {( + [ + ['--radius-sm', activePreset.radiusScale.sm, 'ds-rounded-sm'], + ['--radius-md', activePreset.radiusScale.md, 'ds-rounded-md'], + ['--radius-lg', activePreset.radiusScale.lg, 'ds-rounded-lg'], + ['--radius-xl', activePreset.radiusScale.xl, 'ds-rounded-xl'], + ['--radius-2xl', activePreset.radiusScale['2xl'], 'ds-rounded-2xl'], + ] as const + ).map(([tokenName, computedText, radiusClass]) => ( +
+
+
+
+
+ {tokenName} +
+
+ {computedText} +
+
+
+ +
+ ))} + + + + + +
+
+ Preset Source — `{activePreset.presetFile}` + + Self-contained CSS token delta for {activePreset.label}. Copy into src/dashkit/dashkit.css or import from {activePreset.presetFile}. + +
+ +
+
+ +
+              {activePreset.cssSnippet}
+            
+
+
+
+
+ ); +} diff --git a/src/partials/HomeExamples.tsx b/src/partials/HomeExamples.tsx index 7f6b778..4529882 100644 --- a/src/partials/HomeExamples.tsx +++ b/src/partials/HomeExamples.tsx @@ -1,14 +1,71 @@ +import { Link } from 'react-router-dom'; +import { FiArrowUpRight } from 'react-icons/fi'; import { Tabs, TabsList, TabsTrigger, TabsContent } from '../dashkit/Tabs/Tabs'; +import { Button } from '../dashkit/Button/Button'; +import { useTheme } from './ThemeContext'; +import { THEME_PRESETS, type ThemePreset } from './themeConfig'; import { ColumnOne } from './home-examples/ColumnOne'; import { ColumnTwo } from './home-examples/ColumnTwo'; import { ColumnThree } from './home-examples/ColumnThree'; import { ColumnFour } from './home-examples/ColumnFour'; export function HomeExamples() { + const { preset, setPreset } = useTheme(); + const activePreset = + THEME_PRESETS.find(p => p.id === preset) ?? THEME_PRESETS[0]; + return ( <> + {/* Live Theme Preset Switcher Bar */} +
+
+ {/* Official Dashkit Tabs (variant="pills") */} +
+ setPreset(val as ThemePreset)} + > + + {THEME_PRESETS.map(item => ( + + + {item.label} + + ))} + + +
+ + {/* Preset Metadata & Docs Action */} +
+ + {activePreset.tagline} + / + {activePreset.radius} + + + +
+
+
+ {/* Mobile Tabbed View (< 768px) */} -
+
Forms @@ -32,7 +89,7 @@ export function HomeExamples() {
{/* Desktop Multi-column Grid (>= 768px) */} -
+
diff --git a/src/partials/ThemeContext.ts b/src/partials/ThemeContext.ts index f55b44b..6ce5f91 100644 --- a/src/partials/ThemeContext.ts +++ b/src/partials/ThemeContext.ts @@ -1,8 +1,8 @@ import { createContext, useContext } from 'react'; -import type { BaseColor, PrimaryColor, DarkBackground } from './themeConfig'; +import type { BaseColor, PrimaryColor, DarkBackground, ThemePreset } from './themeConfig'; export type Theme = 'light' | 'dark'; -export type { BaseColor, PrimaryColor, DarkBackground }; +export type { BaseColor, PrimaryColor, DarkBackground, ThemePreset }; export interface ThemeContextType { theme: Theme; @@ -13,6 +13,8 @@ export interface ThemeContextType { setPrimaryColor: (primary: PrimaryColor) => void; darkBg: DarkBackground; setDarkBg: (bg: DarkBackground) => void; + preset: ThemePreset; + setPreset: (preset: ThemePreset) => void; } export const ThemeContext = createContext(undefined); diff --git a/src/partials/ThemeCustomizer.tsx b/src/partials/ThemeCustomizer.tsx index badffbb..fa93dbd 100644 --- a/src/partials/ThemeCustomizer.tsx +++ b/src/partials/ThemeCustomizer.tsx @@ -2,6 +2,7 @@ import { useTheme } from './ThemeContext'; import { BASE_COLORS, PRIMARY_COLORS, + THEME_PRESETS, type BaseColor, type PrimaryColor, } from './themeConfig'; @@ -23,13 +24,14 @@ export function ThemeCustomizer({ className, size = 'sm' }: ThemeCustomizerProps setBaseColor, primaryColor, setPrimaryColor, + preset, + setPreset, } = useTheme(); - const isDefault = baseColor === 'zinc' && primaryColor === 'blue'; + const isDefault = preset === 'default' && baseColor === 'zinc' && primaryColor === 'blue'; const handleReset = () => { - setBaseColor('zinc'); - setPrimaryColor('blue'); + setPreset('default'); }; return ( @@ -44,12 +46,57 @@ export function ThemeCustomizer({ className, size = 'sm' }: ThemeCustomizerProps /> - +
{/* Header */}

Theme Customizer

-

Adjust base & brand colors in real-time

+

Switch design presets & palettes in real-time

+
+ + {/* Preset Section */} +
+
+ + Style Preset + + + {THEME_PRESETS.find(p => p.id === preset)?.label} + +
+
+ {THEME_PRESETS.map(p => { + const isSelected = preset === p.id; + return ( + + ); + })} +
{/* Primary Color Section */} diff --git a/src/partials/ThemeProvider.tsx b/src/partials/ThemeProvider.tsx index 46016d9..5c2a7dd 100644 --- a/src/partials/ThemeProvider.tsx +++ b/src/partials/ThemeProvider.tsx @@ -5,7 +5,9 @@ import { type BaseColor, type PrimaryColor, type DarkBackground, + type ThemePreset, } from './ThemeContext'; +import { THEME_PRESETS } from './themeConfig'; /** * Disables CSS transitions globally for a single tick during theme / palette switches. @@ -68,6 +70,14 @@ export function ThemeProvider({ children }: { children: ReactNode }) { return 'midnight'; }); + const [preset, setPresetState] = useState(() => { + if (typeof window !== 'undefined') { + const saved = localStorage.getItem('dashkit-preset') as ThemePreset; + if (saved && THEME_PRESETS.some(p => p.id === saved)) return saved; + } + return 'default'; + }); + const setBaseColor = (base: BaseColor) => { const restore = disableTransitions(); setBaseColorState(base); @@ -95,6 +105,40 @@ export function ThemeProvider({ children }: { children: ReactNode }) { restore(); }; + const setPreset = (nextPreset: ThemePreset) => { + const restore = disableTransitions(); + const config = THEME_PRESETS.find(p => p.id === nextPreset) ?? THEME_PRESETS[0]; + const root = window.document.documentElement; + + setPresetState(nextPreset); + root.setAttribute('data-preset', nextPreset); + localStorage.setItem('dashkit-preset', nextPreset); + + setBaseColorState(config.baseColor); + root.setAttribute('data-base', config.baseColor); + localStorage.setItem('dashkit-base-color', config.baseColor); + + setPrimaryColorState(config.primaryColor); + root.setAttribute('data-primary', config.primaryColor); + localStorage.setItem('dashkit-primary-color', config.primaryColor); + + setDarkBgState(config.darkBg); + root.setAttribute('data-dark-bg', config.darkBg); + localStorage.setItem('dashkit-dark-bg', config.darkBg); + + if (config.preferredTheme) { + setTheme(config.preferredTheme); + if (config.preferredTheme === 'dark') { + root.classList.add('dark'); + } else { + root.classList.remove('dark'); + } + localStorage.setItem('theme', config.preferredTheme); + } + + restore(); + }; + const toggleTheme = () => { const restore = disableTransitions(); setTheme(prev => { @@ -122,6 +166,7 @@ export function ThemeProvider({ children }: { children: ReactNode }) { root.setAttribute('data-base', baseColor); root.setAttribute('data-primary', primaryColor); root.setAttribute('data-dark-bg', darkBg); + root.setAttribute('data-preset', preset); }, []); return ( @@ -135,6 +180,8 @@ export function ThemeProvider({ children }: { children: ReactNode }) { setPrimaryColor, darkBg, setDarkBg, + preset, + setPreset, }} > {children} diff --git a/src/partials/home-examples/ColumnOne.tsx b/src/partials/home-examples/ColumnOne.tsx index fe1b9d5..4b53499 100644 --- a/src/partials/home-examples/ColumnOne.tsx +++ b/src/partials/home-examples/ColumnOne.tsx @@ -473,7 +473,7 @@ export function ColumnOne() {
- + Delivery Active (200 OK)