Skip to content
This repository was archived by the owner on Mar 7, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/project_plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ A pragmatic breakdown into **four one‑week sprints** plus a preparatory **Spri
| 1.2a | Set up basic Storybook configuration. | `npm run storybook` starts successfully. | ✓ |
| 1.2b | Install and configure Storybook addon‑docs. | Documentation tab shows component documentation. | ✓ |
| 1.2c | Configure and verify Storybook addon‑a11y. | axe‑a11y addon shows zero violations. | ✓ |
| 1.3 | Add global theme bridging (`theme.css` ↔ DaisyUI). | Cypress visual diff (light/dark) matches golden images. | |
| 1.3 | Add global theme bridging (`theme.css` ↔ DaisyUI). | Cypress visual diff (light/dark) matches golden images. | |
| 1.4 | Create first **AuthShell** layout with logo slot. | Rendered via Storybook; Playwright snapshot approved. | |
| 1.5 | Document design tokens (`DESIGN_TOKENS.md`). | File exists; CI step `tokens-check` verifies presence of each CSS var. | |

Expand Down
51 changes: 47 additions & 4 deletions packages/ui-kit/.storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import React from 'react'
import React, { useEffect } from 'react'
import type { Preview } from '@storybook/react'
import { themes } from '@storybook/theming'
import '../src/styles/globals.css'
import { initializeTheme } from '../src/theme'

// Theme switcher
const ThemeInitializer = ({ children }: { children: React.ReactNode }) => {
useEffect(() => {
// Initialize theme when component mounts
initializeTheme()
}, [])

return <>{children}</>
}

const preview: Preview = {
parameters: {
Expand All @@ -18,14 +30,45 @@ const preview: Preview = {
disable: false,
},
layout: 'centered',
darkMode: {
dark: { ...themes.dark },
light: { ...themes.light },
current: 'light',
},
},
decorators: [
(Story) => (
<div className="p-4">
<Story />
</div>
<ThemeInitializer>
<div className="p-4">
<Story />
</div>
</ThemeInitializer>
),
],
globalTypes: {
theme: {
name: 'Theme',
description: 'Global theme for components',
defaultValue: 'light',
toolbar: {
icon: 'circlehollow',
items: [
{ value: 'light', icon: 'sun', title: 'Light' },
{ value: 'dark', icon: 'moon', title: 'Dark' },
],
showName: true,
dynamicTitle: true,
onChange: (theme) => {
const isDark = theme === 'dark'
if (isDark) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
}
},
},
},
}

export default preview
12 changes: 12 additions & 0 deletions packages/ui-kit/cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineConfig } from 'cypress';

export default defineConfig({
e2e: {
baseUrl: 'http://localhost:6006', // Storybook default port
specPattern: 'cypress/e2e/**/*.cy.{js,jsx,ts,tsx}',
screenshotOnRunFailure: true,
},
viewportWidth: 1280,
viewportHeight: 720,
video: false,
});
28 changes: 28 additions & 0 deletions packages/ui-kit/cypress/e2e/theme.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
describe('Theme Visual Tests', () => {
// Test Button component in light and dark mode
it('Button component - Light Mode', () => {
// Visit the Storybook directly first
cy.visit('/');
cy.contains('Button').should('be.visible');
cy.log('Storybook is running and accessible');

// Then go to a specific story in iframe mode
cy.visit('/iframe.html?id=primitives-button--primary&viewMode=story');
cy.get('body').should('be.visible');
cy.document().then((doc) => {
doc.documentElement.classList.remove('dark');
});
cy.wait(500); // Wait for theme to apply
cy.screenshot('button-light-mode', { overwrite: true });
});

it('Button component - Dark Mode', () => {
cy.visit('/iframe.html?id=primitives-button--primary&viewMode=story');
cy.get('body').should('be.visible');
cy.document().then((doc) => {
doc.documentElement.classList.add('dark');
});
cy.wait(500); // Wait for theme to apply
cy.screenshot('button-dark-mode', { overwrite: true });
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions packages/ui-kit/cypress/support/e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Simplified support file for testing
// Using Cypress's built-in screenshot functionality

// Prevent tests from failing on uncaught exceptions
Cypress.on('uncaught:exception', () => false);
8 changes: 7 additions & 1 deletion packages/ui-kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
"test": "vitest",
"test:coverage": "vitest run --coverage",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
"build-storybook": "storybook build",
"cy:open": "cypress open",
"cy:run": "cypress run",
"theme-screenshots": "cypress run --spec \"cypress/e2e/theme.cy.ts\""
},
"dependencies": {
"@radix-ui/react-slot": "^1.0.2",
Expand Down Expand Up @@ -52,6 +55,7 @@
"@storybook/react": "^8.6.14",
"@storybook/react-vite": "^8.6.14",
"@storybook/test": "^8.6.14",
"@storybook/theming": "^8.6.14",
"@storybook/types": "^8.6.14",
"@testing-library/dom": "^10.0.0",
"@testing-library/jest-dom": "^6.6.3",
Expand All @@ -62,6 +66,8 @@
"@types/testing-library__react": "^10.2.0",
"@vitejs/plugin-react": "^4.4.1",
"autoprefixer": "^10.4.21",
"cypress": "^14.3.3",
"cypress-visual-regression": "^5.3.0",
"daisyui": "^5.0.35",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^5.2.0",
Expand Down
18 changes: 4 additions & 14 deletions packages/ui-kit/src/styles/globals.css
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
/* Import theme variables */
@import "../theme/theme.css";

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
:root {
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;

--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;

--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
}
}
@tailwind utilities;
109 changes: 109 additions & 0 deletions packages/ui-kit/src/theme/DESIGN_TOKENS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Design Tokens

This document provides an overview of the design tokens used in the UI-Kit. These tokens serve as the foundation for the theming system, bridging DaisyUI and Shadcn UI.

## Color Tokens

### Base Colors

| Token | DaisyUI Variable | Description |
| -------------------------- | ----------------- | ----------------------------------- |
| `--primary` | `hsl(var(--p))` | Primary brand color |
| `--primary-foreground` | `hsl(var(--pc))` | Text color for primary elements |
| `--secondary` | `hsl(var(--s))` | Secondary color |
| `--secondary-foreground` | `hsl(var(--sc))` | Text color for secondary elements |
| `--accent` | `hsl(var(--a))` | Accent color for highlights |
| `--accent-foreground` | `hsl(var(--ac))` | Text color for accent elements |
| `--destructive` | `hsl(var(--er))` | Error/destructive action color |
| `--destructive-foreground` | `hsl(var(--erc))` | Text color for destructive elements |

### UI Element Colors

| Token | DaisyUI Variable | Description |
| ---------------------- | ----------------------------------- | ------------------------ |
| `--background` | `hsl(var(--b1))` / `hsl(var(--n))` | Page background color |
| `--foreground` | `hsl(var(--bc))` / `hsl(var(--nc))` | Main text color |
| `--card` | `hsl(var(--b1))` / `hsl(var(--n))` | Card background color |
| `--card-foreground` | `hsl(var(--bc))` / `hsl(var(--nc))` | Card text color |
| `--popover` | `hsl(var(--b1))` / `hsl(var(--n))` | Popover background color |
| `--popover-foreground` | `hsl(var(--bc))` / `hsl(var(--nc))` | Popover text color |

### Border and Input Colors

| Token | DaisyUI Variable | Description |
| ---------- | ----------------------------------- | -------------------------- |
| `--border` | `hsl(var(--b2))` / `hsl(var(--b3))` | Border color |
| `--input` | `hsl(var(--b2))` / `hsl(var(--b3))` | Input element border color |
| `--ring` | `hsl(var(--p))` | Focus ring color |

### Status Colors

| Token | DaisyUI Variable | Description |
| ---------------------- | ----------------- | -------------------------- |
| `--success` | `hsl(var(--su))` | Success/confirmation color |
| `--success-foreground` | `hsl(var(--suc))` | Text on success elements |
| `--warning` | `hsl(var(--wa))` | Warning color |
| `--warning-foreground` | `hsl(var(--wac))` | Text on warning elements |
| `--info` | `hsl(var(--in))` | Information color |
| `--info-foreground` | `hsl(var(--inc))` | Text on info elements |

## Other Design Tokens

### Border Radius

| Token | Value | Description |
| ---------- | -------- | ------------------ |
| `--radius` | `0.5rem` | Base border radius |

### Shadows

| Token | Description |
| ------------- | -------------- |
| `--shadow-sm` | Small shadow |
| `--shadow` | Default shadow |
| `--shadow-md` | Medium shadow |
| `--shadow-lg` | Large shadow |

## Theme Switching

The UI-Kit supports both light and dark themes. In dark mode, background and foreground colors are adjusted to provide appropriate contrast.

### Light Mode (Default)

In light mode, backgrounds use `--b1` (base-100) colors from DaisyUI, which are typically lighter shades.

### Dark Mode

Dark mode is activated by adding the `dark` class to the document's root element. In dark mode:

- Backgrounds use `--n` (neutral) colors
- Text uses `--nc` (neutral-content) colors
- Elements have increased contrast for better visibility

## Usage

Import the theme CSS in your application:

```tsx
import "@org/ui-kit/theme/theme.css";
```

To toggle between themes:

```tsx
import { toggleTheme } from "@org/ui-kit";

// In a button click handler:
onClick = { toggleTheme };
```

To initialize theme based on user preference:

```tsx
import { initializeTheme } from "@org/ui-kit";

// In your app initialization:
useEffect(() => {
initializeTheme();
}, []);
```
29 changes: 27 additions & 2 deletions packages/ui-kit/src/theme/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,27 @@
// Theme utilities will be exported here
export { }
import './theme.css';

// Theme toggle utility
export const toggleTheme = () => {
const isDark = document.documentElement.classList.contains('dark');
if (isDark) {
document.documentElement.classList.remove('dark');
localStorage.setItem('theme', 'light');
} else {
document.documentElement.classList.add('dark');
localStorage.setItem('theme', 'dark');
}
};

// Initialize theme based on local storage or system preference
export const initializeTheme = () => {
const savedTheme = localStorage.getItem('theme');

if (savedTheme) {
if (savedTheme === 'dark') {
document.documentElement.classList.add('dark');
}
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.classList.add('dark');
localStorage.setItem('theme', 'dark');
}
};
Loading