Skip to content
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
7 changes: 7 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { StorybookConfig } from '@storybook/react-vite';
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';

const config: StorybookConfig = {
stories: [
Expand Down Expand Up @@ -30,6 +31,12 @@ const config: StorybookConfig = {
typescript: {
reactDocgen: 'react-docgen-typescript',
},

async viteFinal(config) {
config.plugins = config.plugins || [];
config.plugins.push(vanillaExtractPlugin());
return config;
},
};

export default config;
58 changes: 52 additions & 6 deletions .storybook/preview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,70 @@ export const parameters = {
},
};

export const globalTypes = {};

const globalStyle = globalCss({
body: {
bc: '$contentBg',
},
});

const VanillaProviderWrapper = ({ children, isDark, primaryColor }) => {
const [Provider, setProvider] = React.useState(null);
const [loading, setLoading] = React.useState(true);

React.useEffect(() => {
import('../styles/themeContext')
.then((module) => {
setProvider(() => module.VanillaExtractThemeProvider);
setLoading(false);
})
.catch((err) => {
console.warn('VanillaExtractThemeProvider failed to load:', err);
setLoading(false);
});
}, []);

if (loading) {
return React.createElement('div', { style: { padding: '24px' } }, 'Loading theme system...');
}

if (Provider) {
return React.createElement(
Provider,
{
forcedTheme: isDark ? 'dark' : 'light',
primaryColor,
},
children,
);
}

// If provider failed to load, just return children (Stitches fallback)
return children;
};

export const decorators = [
(renderStory) => {
const [isDark, setDark] = React.useState(false);

React.useEffect(() => {
darkTheme('blue').toString();
lightTheme('blue').toString();
}, []);

React.useEffect(() => {
darkTheme('neon').toString();
lightTheme('neon').toString();
channel.on(DARK_MODE_EVENT_NAME, setDark);
return () => channel.removeListener(DARK_MODE_EVENT_NAME, setDark);
}, []);

return (
<FaencyProvider>
{globalStyle()}
{renderStory()}
</FaencyProvider>
<VanillaProviderWrapper isDark={isDark} primaryColor="blue">
<FaencyProvider>
{globalStyle()}
{renderStory()}
</FaencyProvider>
</VanillaProviderWrapper>
);
},
];
Expand Down
100 changes: 100 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Overview

Faency is a React component library and design system for Traefik Labs, built with React, TypeScript, Stitches CSS-in-JS, and Radix UI Primitives. It provides accessible, themed components with light/dark mode support.

## Development Commands

### Core Development

- `yarn install` - Install dependencies
- `yarn patch-package` - Apply necessary patches to Stitches after installation
- `yarn storybook` - Start Storybook development server at http://localhost:6006
- `yarn build` - Build the library for production
- `yarn lint` - Lint TypeScript/React files in components directory
- `yarn lint:fix` - Auto-fix linting issues
- `yarn test` - Run Jest tests in watch mode
- `yarn test:ci` - Run tests in CI mode (silent, non-watch)

### Release

- `yarn release` - Build and publish using semantic-release

## Architecture

### Styling System

- **Stitches CSS-in-JS**: Core styling system with design tokens and utilities
- **Design Tokens**: Defined in `stitches.config.ts` with semantic color system
- **Theme Support**: Light/dark themes with customizable primary colors
- **Component Themes**: Each component has its own theme file (e.g., `Button.themes.ts`)

### Component Structure

Components follow a consistent pattern:

- `ComponentName.tsx` - Main component implementation
- `ComponentName.stories.tsx` - Storybook stories
- `ComponentName.test.tsx` - Jest tests (where applicable)
- `ComponentName.themes.ts` - Theme definitions for light/dark modes
- `index.ts` - Exports

### Key Files

- `stitches.config.ts` - Stitches configuration with design tokens, utilities, and theme setup
- `colors/` - Color palette definitions (light/dark variants)
- `index.ts` - Main library exports
- `components/FaencyProvider.tsx` - Root provider component

### Colors and Theming

- Primary colors: `neon`, `blue`, `orange`, `red`, `green`, `deepBlue`, `grayBlue`
- Dynamic theme generation based on primary color selection
- Component themes support both light and dark variants
- Colors are defined in `/colors/` with systematic naming

### Component Dependencies

- Built on Radix UI Primitives for accessibility
- Uses Stitches for styling with CSS-in-JS
- React 18+ and TypeScript 5+ required

## Development Workflow

### Code Style

- **No Unnecessary Comments**: Do not add obvious or redundant comments. Code should be self-explanatory through clear naming and structure. Only add comments when explaining complex logic, non-obvious decisions, or documenting public APIs.
- **No Unnecessary Emojis**: OK for console warnings/errors, but avoid in code comments or commit messages.

### Adding New Components

1. Create component directory in `/components/`
2. Implement main component with Stitches styling
3. Create theme file with `getLight()` and `getDark()` functions
4. Add Storybook stories with all variants
5. Write Jest tests for critical functionality
6. Export from main `index.ts`

### Working with Themes

- Theme objects must have `getLight(primaryColor)` and `getDark(primaryColor)` methods
- Register new themes in `stitches.config.ts` in both light and dark theme functions
- Use semantic color tokens for consistency

### Testing Strategy

- Jest with React Testing Library setup
- jsdom environment for DOM testing
- Focus on accessibility and component behavior
- Run tests before submitting changes

## Important Notes

- **Patch Required**: Run `yarn patch-package` after installing dependencies to fix Stitches TypeScript 5 compatibility
- **Node Version**: Requires Node.js 20+
- **Conventional Commits**: Follow conventional commit format for semantic releases
- **Accessibility**: All components must be accessible and support keyboard navigation
- **Storybook**: Every component requires comprehensive stories showing all variants
33 changes: 31 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ npm install @traefiklabs/faency@next
yarn add @traefiklabs/faency@next
```

Then you need to wire up the FaencyProvider which will hold the context with the Theme configuration and everything global that the components will need to work well.
#### Using Stitches Components (Current)

Wire up the FaencyProvider which will hold the context with the Theme configuration:

> The provider accepts one parameter besides the `children`, which is the `primaryColor`, that will be used to build the colors used on the Theme. This color can be one of the colors exported by the `Stitches` config, just by adding `$` as a prefix, or can be any string that represents a CSS color.

Expand All @@ -44,9 +46,36 @@ const Container = styled(Flex, {
const MyComponent = () => <Container>{children}</Container>;
```

#### Using Vanilla Extract Components (New - Recommended)

For better performance with static CSS, use the new Vanilla Extract components:

1. Import the CSS file in your app's entry point:

```jsx
import '@traefiklabs/faency/dist/style.css';
```

2. Wrap your app with the VanillaExtractThemeProvider:

```jsx
import { VanillaExtractThemeProvider } from '@traefiklabs/faency';
import { BoxVanilla, BadgeVanilla } from '@traefiklabs/faency';

const App = () => (
<VanillaExtractThemeProvider defaultTheme="light" primaryColor="blue">
<BoxVanilla css={{ p: '$4' }}>
<BadgeVanilla variant="blue">Hello Faency!</BadgeVanilla>
</BoxVanilla>
</VanillaExtractThemeProvider>
);
```

> **Note**: Vanilla Extract components use static CSS generated at build time, providing better performance than runtime CSS-in-JS. Components with the `Vanilla` suffix (e.g., `BadgeVanilla`, `BoxVanilla`) require the CSS import above.

## How to contribute

- Make sure you have Node 12+, or if you prefer, you can work in a Docker container:
- Make sure you have Node 18+, or if you prefer, you can work in a Docker container:

```sh
docker run -it -v $(pwd):/usr/local/src/ -w /usr/local/src/ -p 3000:3000 node:latest bash
Expand Down
Loading