
Accelint Design Foundation
- 114 installs
- 21 repo stars
- Updated August 4, 2026
- gohypergiant/agent-skills
accelint-design-foundation is a Claude agent skill that encodes Accelint design-system styling rules so developers can theme components with semantic tokens and CSS modules correctly.
About
accelint-design-foundation is an Accelint agent skill for styling with @accelint/design-foundation and @accelint/design-toolkit using opinionated Tailwind conventions, semantic tokens, CSS modules, and @variant blocks. Reach for it when you are theming React components, editing .module.css files, or aligning spacing and colors with the Accelint design system instead of vanilla Tailwind defaults. It encodes hard NEVER rules and a token selection framework so implementations stay theme-safe and maintainable.
- Semantic tokens (bg-surface-default, fg-primary-bold) over raw Tailwind colors
- Custom spacing scale (p-m, gap-s) with 1:1 numeric fallback only when needed
- Outline-based borders and @variant directive blocks in CSS modules
- Required @reference and PostCSS plugin for CSS module resolution
- NEVER list for inline Tailwind, multiple @apply, and clsx import path
Accelint Design Foundation by the numbers
- 114 all-time installs (skills.sh)
- Ranked #1,077 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/gohypergiant/agent-skills --skill accelint-design-foundationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 114 |
|---|---|
| repo stars | ★ 21 |
| Last updated | August 4, 2026 |
| Repository | gohypergiant/agent-skills ↗ |
How do you style Accelint components without breaking themes, layout sizing, or CSS module variant conventions?
Applies @accelint/design-foundation and design-toolkit rules for semantic tokens, CSS modules, @variant blocks, and Tailwind styling that matches Accelint conventions.
Who is it for?
Frontend developers using @accelint/design-foundation or @accelint/design-toolkit who work in CSS modules and need design-system-correct Tailwind usage.
Skip if: Projects on vanilla Tailwind without Accelint packages or teams that prefer only inline utility classes for all component styling.
When should I use this skill?
Users say style this, theme this component, add spacing or colors, work with @variant, or edit .module.css with design foundation packages.
What you get
Component styles that follow semantic tokens, spacing scale, outline borders, layered @apply rules, and validated PostCSS and @reference setup.
Files
Accelint Design Foundation
Expert knowledge for styling with @accelint/design-foundation and @accelint/design-toolkit — opinionated Tailwind conventions that differ from vanilla implementations.
NEVER Do When Styling with Design Foundation
- NEVER use numeric spacing classes as first choice - Strongly prefer the semantic scale:
p-xxs,gap-m,m-l. Numeric classes likep-4,gap-6work with a 1:1 relationship (p-1= 1px, NOT 4px like vanilla Tailwind), but should only be used for rare cases where implementing non-conforming designs. The semantic scale provides design system consistency. - NEVER use manual theme handling with raw color values - Avoid
dark:bg-gray-900orclassName={theme === 'dark' ? 'bg-black' : 'bg-white'}. Use semantic color classes likebg-surface-defaultandfg-primary-boldthat automatically adapt to light/dark themes. - NEVER use borders for sizing elements - Use
outlineinstead ofborderclasses. Borders add to element dimensions (breaks layouts), while outlines overlay without affecting size. Elements should size consistently based on content and padding only. - NEVER use arbitrary Tailwind variants - Arbitrary values like
hover:[&>svg]:opacity-50break the design system. Use supported React Aria variants or conditional class rendering withclsx. - NEVER bypass CSS layers when styling components - Use
@layer components.l1,@layer components.l2for cascade hierarchy. Bypassing layers causes specificity wars and makes overrides unpredictable. - NEVER use primitive/domain tokens as first choice - Strongly prefer semantic tokens (
bg-surface-default,fg-primary-bold) in components. The utility classes (bg-*,fg-*,icon-*,outline-*) provide fallback access todomain-*andprimitive-*tokens for rare cases where designs go beyond the design system, but this should be exceptional. Semantic tokens provide theming flexibility and design system consistency. - NEVER use inline Tailwind classes for component styling - Component styles belong in CSS modules, not inline className props. Inline Tailwind should only be used for minor one-off overrides. Using inline classes for all component styling creates unmaintainable code and loses the benefits of CSS modules (scoping, organization, reusability).
- NEVER use multiple @apply directives in a single CSS rule - Group all Tailwind classes into a single
@applystatement. Multiple@applydirectives prevent Tailwind IDE plugins from sorting classes and identifying issues. Write@apply bg-surface-default outline-1 outline-interactive p-m;not separate@applylines. - NEVER use attribute selectors for variants in CSS modules - Use
@variantdirective blocks, not attribute selectors like[data-size="small"]. Write@variant size-small { @apply p-s; }not.button[data-size="small"] { @apply p-s; }. The@variantdirective automatically applies styles when the matching data attribute is present. - NEVER use Tailwind's default theme values - The design foundation removes and replaces Tailwind defaults. Relying on default shadows, font sizes, or colors will break. Use only the semantic classes provided by the design system.
- NEVER omit @reference directive in CSS modules - Every CSS module file must include
@reference '#globals';(if custom entrypoint exists) or@reference '@accelint/design-foundation/styles';at the top. Without this, semantic tokens and @variant blocks are undefined, causing build errors. - NEVER skip PostCSS configuration - The
@accelint/postcss-tailwind-css-modulesplugin is required inpostcss.config.mjs. Without it, named group selectors (likegroup-hover/button:) and @variant selectors fail to resolve in CSS modules. - NEVER import clsx directly from 'clsx' package - Always import from
@accelint/design-foundation/lib/utilsinstead:import { clsx } from '@accelint/design-foundation/lib/utils';. The design foundation re-exports clsx with additional type support and design system integration. Importing directly bypasses these enhancements.
Before Styling a Component, Ask
Apply these tests to ensure styling aligns with the design system:
Theme Compatibility
- Will this work in both light and dark themes? Use semantic color tokens that adapt automatically. Test by toggling between
@variant lightand@variant dark. - Am I using raw color values? If yes, replace with semantic tokens. Raw values don't theme.
Token System
- Am I using the correct token type? Strongly prefer semantic tokens (
bg-surface-default,fg-primary-bold). Only usedomain-*orprimitive-*fallbacks for exceptional cases where design goes beyond the system. - Is there a semantic token for this? Check the token catalog first. If no semantic token exists and the design genuinely requires it, fallback to
domain-*orprimitive-*is acceptable but rare.
Token Selection Framework
When choosing a token, follow this decision tree: 1. Identify element purpose - Is this a surface, text, icon, or outline? 2. Determine hierarchy - Primary, secondary, or tertiary emphasis? 3. Consider state - Default, hover, active, disabled? 4. Check status - Info, success, warning, danger?
Example: "I need text color for a primary heading" → Purpose: text (fg-*) → Hierarchy: primary with emphasis (primary-bold) → Result: fg-primary-bold
Example: "I need background for an interactive button in hover state" → Purpose: background (bg-*) → State: interactive hover (interactive-bold-hover) → Result: bg-interactive-bold-hover
Spacing System
- Does this spacing value exist in the semantic scale? Use
xxs/xs/s/m/l/xl/xxl/oversizedscale. If a value isn't in the scale, question whether it's needed or use the closest semantic value. - Need a non-standard spacing value? Numeric classes (
p-1,m-12) work with 1:1 relationship (p-1 = 1px, NOT 4px), but only use for rare non-conforming design cases. Semantic scale is strongly preferred for consistency.
Variant Usage
- Can this be expressed with data attributes? Use
data-color="info"with supported variants instead of arbitrary classes. - Am I overriding Design Toolkit components correctly? Use
classNameorclassNamesprops, not custom CSS files.
Layout Impact
- Will outlines work here or do I need borders? Outlines work for most cases. Borders are only needed when the border must affect layout dimensions.
Styling Approach
- Should this be in CSS modules or inline? Component styles belong in CSS modules (.module.css). Only use inline Tailwind classes for minor one-off overrides or adjustments.
- Is this a reusable component or one-off instance? Reusable components require CSS modules. One-off instances can use inline classes for small tweaks.
Setup Verification
- Is PostCSS configured correctly? Check that
@accelint/postcss-tailwind-css-modulesplugin is inpostcss.config.mjs. Without it, named groups and @variant selectors won't work in CSS modules. - Does every CSS module have @reference? Each
.module.cssfile must reference either'#globals'(custom entrypoint) or'@accelint/design-foundation/styles'at the top. - Is the CSS entrypoint imported first? Custom globals.css (or design-foundation/styles) must be the first import in the root layout.
Setup Requirements
CRITICAL: Design foundation requires specific PostCSS and CSS module configuration to work correctly.
PostCSS Configuration
Create or update postcss.config.mjs in project root:
export default {
plugins: {
'@tailwindcss/postcss': {},
'@accelint/postcss-tailwind-css-modules': {}, // Required for CSS modules
},
};Why: The @accelint/postcss-tailwind-css-modules plugin fixes named group resolution in CSS module selectors (e.g., group-hover/button:) and @variant selectors. Without it, these selectors fail to resolve correctly in CSS module files.
Package.json Imports (If Custom CSS Entrypoint Exists)
If the project implements a custom CSS entrypoint for token/utility configuration:
{
"imports": {
"#globals": "./src/styles/globals.css"
}
}Purpose: Allows CSS modules to reference the custom entrypoint via @reference '#globals';
CSS Module Reference Pattern
Every CSS module file must include a reference directive:
/* If project has custom CSS entrypoint (defined in package.json imports): */
@reference '#globals';
@layer components.l1 {
.button {
@apply px-m py-xs;
}
}/* If NO custom CSS entrypoint, reference design-foundation directly: */
@reference '@accelint/design-foundation/styles';
@layer components.l1 {
.button {
@apply px-m py-xs;
}
}Why: The @reference directive imports the design system's tokens, utilities, and variant definitions. Without it, semantic tokens and @variant blocks are undefined.
Custom CSS Entrypoint (Optional)
If implementing custom tokens or utilities, create a CSS entrypoint (e.g., src/styles/globals.css):
/* Import design foundation base */
@import "@accelint/design-foundation/styles";
/* Add custom token overrides or utilities here */
@theme {
--custom-brand-color: #ff0000;
}Then: Import this file as the first import in your root layout component:
import './styles/globals.css'; // First import
import { ReactNode } from 'react';
export default function RootLayout({ children }: { children: ReactNode }) {
return <html>{children}</html>;
}How to Use
This skill uses progressive disclosure to minimize context usage:
1. Start with Core Patterns (SKILL.md)
Follow the styling patterns and token usage below for consistent implementation.
2. Reference Token Catalog (AGENTS.md)
Load AGENTS.md for quick reference of available tokens, spacing scale, and variant patterns.
3. Load Detailed References as Needed
MANDATORY loading triggers - Load these references in specific scenarios:
Setting up design foundation for the first time:
- MANDATORY: Load references/setup.md (~8.9K) completely when user says "setup design foundation", "configure design foundation", "install design foundation", or encounters build errors like "undefined variable" or "@variant not found"
- Do NOT Load: token-reference.md, variant-system.md, spacing-scale.md, migration-guide.md
Choosing tokens or understanding token hierarchy:
- MANDATORY: Load references/token-reference.md (~6.5K) when uncertain which semantic token to use or when user needs complete token catalog
- Do NOT Load: setup.md (unless build errors), migration-guide.md (unless migrating)
Working with @variant system or component variants:
- MANDATORY: Load references/variant-system.md (~5.8K) when implementing data attribute variants or working with React Aria states
- Do NOT Load: setup.md (unless build errors), migration-guide.md (unless migrating)
Understanding spacing scale or numeric fallbacks:
- MANDATORY: Load references/spacing-scale.md (~8.5K) when confused about semantic vs numeric spacing or need complete spacing catalog
- Do NOT Load: token-reference.md (unless also working with colors), setup.md (unless build errors)
Migrating from vanilla Tailwind:
- MANDATORY: Load references/migration-guide.md (~4.2K) when converting existing Tailwind code to design foundation conventions
- Do NOT Load: Other references unless specific issues arise after migration
Troubleshooting build errors or setup issues:
- MANDATORY: Load references/setup.md completely when encountering errors like "undefined variable", "@variant not found", "group-hover/button: not working"
- Do NOT Load: Other references until setup is confirmed working
Styling Patterns
CSS Modules for Component Styling
Default approach: Component styles in CSS modules with @apply directives.
user-card.module.css:
@layer components.l1 {
/* ✅ Single @apply per rule - enables IDE plugin support */
.card {
@apply bg-surface-default outline-1 outline-interactive shadow-elevation-raised-muted p-m;
}
.header {
@apply flex items-center justify-between mb-s;
}
.title {
@apply fg-primary-bold text-body-l;
}
.content {
@apply space-y-xs mb-m;
}
.email {
@apply fg-primary-bold text-body-m;
}
.role {
@apply fg-primary-muted text-body-s;
}
.actions {
@apply flex gap-s;
}
}
/* ❌ Wrong - multiple @apply directives break IDE plugins */
/*
.card {
@apply bg-surface-default;
@apply outline-1 outline-interactive;
@apply shadow-elevation-raised-muted;
@apply p-m;
}
*/user-card.tsx:
import styles from './user-card.module.css';
export function UserCard({ name, email, role }) {
return (
<article className={styles.card}>
<header className={styles.header}>
<h3 className={styles.title}>{name}</h3>
</header>
<div className={styles.content}>
<p className={styles.email}>{email}</p>
<p className={styles.role}>{role}</p>
</div>
</article>
);
}Inline classes only for one-off overrides:
// ✅ Correct - CSS module + inline override for specific instance
<UserCard className="mb-xl" /> {/* One-off spacing adjustment */}
// ❌ Wrong - all styling inline
<div className="bg-surface-default outline-1 outline-interactive shadow-elevation-raised-muted p-m">
<h3 className="fg-primary-bold text-body-l">{name}</h3>
</div>Conditional classes with clsx:
// ✅ Correct - import clsx from design foundation
import { clsx } from '@accelint/design-foundation/lib/utils';
import styles from './Button.module.css';
export function Button({ variant, isActive }) {
return (
<button className={clsx(styles.button, isActive && styles.active)}>
Click me
</button>
);
}
// ❌ Wrong - importing directly from clsx package
import clsx from 'clsx';Token Categories
*Background tokens (`bg-`):**
bg-surface-default- Primary surface (page/card background)bg-surface-raised- Raised/elevated surfacebg-interactive-bold- Primary action backgroundbg-info-muted- Info message backgroundsbg-advisory-muted- Warning message backgroundsbg-critical-muted- Error/danger message backgroundsbg-normal-muted- Success message backgrounds
*Foreground tokens (`fg-`):**
fg-primary-bold- Primary text (headlines, body text)fg-primary-muted- Subtle text (captions, secondary content)fg-inverse-bold- Text on dark/colored backgroundsfg-accent-primary-bold- Interactive text/linksfg-critical-bold- Error/danger text
*Outline tokens (`outline-`):**
outline-interactive- Interactive outlines (buttons, inputs)outline-static- Static outlines (borders, dividers)outline-info-bold- Info state outlinesoutline-advisory-bold- Warning state outlinesoutline-critical-bold- Error/danger state outlines
Token fallback pattern:
// ✅ Preferred - semantic tokens
<div className="bg-surface-default fg-primary-bold">Content</div>
// ⚠️ Acceptable but rare - domain/primitive fallback for edge cases
<div className="bg-domain-brand fg-primitive-blue-500">
{/* Use only when design exceeds system */}
</div>
// ❌ Wrong - using fallbacks when semantic tokens exist
<div className="bg-primitive-neutral-100 fg-primitive-neutral-900">
{/* Should use bg-surface-default fg-primary-bold */}
</div>Spacing Scale
Eight-step semantic scale: xxs → xs → s → m → l → xl → xxl → oversized
Think semantic meaning, not pixels. m means "medium for this context", not a specific pixel value.
CSS module usage:
.button {
@apply px-m py-xs; /* ✅ Semantic spacing - preferred */
}
.card {
@apply p-m space-y-s; /* ✅ Semantic scale for consistency */
}
/* ⚠️ Acceptable but rare - numeric fallback for edge cases */
.nonStandardLayout {
@apply p-13; /* 13px exactly - use only for non-conforming designs */
/* Note: p-1 = 1px (not 4px like vanilla Tailwind) */
}Spacing fallback pattern:
- Preferred: Use semantic scale (
p-m,gap-s,mb-l) - Fallback: Numeric classes available with 1:1 relationship (p-1 = 1px, p-12 = 12px)
- When to use numeric: Only for non-conforming designs where semantic scale doesn't fit
Outlines vs Borders
Prefer outlines - they don't affect element dimensions.
/* ✅ Correct - outline doesn't impact layout */
.card {
@apply outline-1 outline-interactive;
}
/* ❌ Wrong - border adds to dimensions */
.card {
@apply border-2 border-gray-300;
}Use borders only when:
- Border must participate in layout (table cells)
- Dimension impact is explicitly desired
Variant System
Use data attributes for component variants:
Button.module.css:
@layer components.l1 {
.button {
@apply px-m py-xs;
}
}
@layer components.l2 {
/* ✅ Use @variant blocks for conditional styling */
.button {
@variant color-info {
@apply bg-interactive-bold fg-inverse-bold;
}
@variant color-critical {
@apply outline-2 outline-critical-bold fg-critical-bold;
}
@variant size-large {
@apply px-l py-s;
}
}
}
/* ❌ Wrong - attribute selectors */
/*
.button[data-color="primary"] {
@apply bg-interactive-bold;
}
*/Button.tsx:
import styles from './Button.module.css';
export function Button({ children, color = 'primary', size = 'medium' }) {
return (
<button
className={styles.button}
data-color={color}
data-size={size}
>
{children}
</button>
);
}Common variants:
data-color:info,success,warning,dangerdata-size:small,medium,largedata-state:active,disabled,loading
CSS Layer Hierarchy
Always use layers in CSS modules:
/* ✅ Correct - layered for predictable specificity */
@layer components.l1 {
.base-styles { /* Base component styles */ }
}
@layer components.l2 {
.variant-styles { /* Variant styles */ }
}
@layer components.l3 {
.state-styles { /* State styles */ }
}
/* ❌ Wrong - no layers causes specificity issues */
.component {
background: var(--bg-surface-default);
}Layer order: l1 (base) < l2 (variants) < l3 (states)
Overriding Design Toolkit Components
Use className prop for overrides:
// ✅ Correct - override with inline class
<DesignToolkitButton className="mb-l">
Action
</DesignToolkitButton>
// ✅ Correct - use classNames prop for internal elements
<DesignToolkitButton classNames={{ icon: 'icon-override' }}>
Action
</DesignToolkitButton>Common Issues
Issue: "undefined variable --bg-surface-default"
Cause: Missing @reference directive in CSS module.
Fix: Add @reference '#globals'; or @reference '@accelint/design-foundation/styles'; at the top of the CSS module file.
/* ✅ Correct */
@reference '@accelint/design-foundation/styles';
@layer components.l1 {
.card {
@apply bg-surface-default; /* Now defined */
}
}Issue: "@variant directive not recognized"
Cause: Missing @reference directive or PostCSS plugin not configured.
Fix 1: Verify @reference directive exists at top of CSS module.
Fix 2: Check postcss.config.mjs includes @accelint/postcss-tailwind-css-modules plugin:
export default {
plugins: {
'@tailwindcss/postcss': {},
'@accelint/postcss-tailwind-css-modules': {}, // Add this
},
};Issue: "group-hover/button: selector not working"
Cause: Missing @accelint/postcss-tailwind-css-modules plugin.
Fix: Add plugin to postcss.config.mjs (see above).
Issue: Styles not applying / tokens showing as raw values
Cause: CSS entrypoint not imported or imported too late.
Fix: Ensure globals.css (or @accelint/design-foundation/styles) is the FIRST import in root layout:
// ✅ Correct order
import './styles/globals.css'; // First
import './other-styles.css';
import Component from './Component';
// ❌ Wrong order
import Component from './Component';
import './styles/globals.css'; // Too lateIssue: "Cannot find module '#globals'"
Cause: Missing package.json imports field or path incorrect.
Fix: Add imports to package.json:
{
"imports": {
"#globals": "./src/styles/globals.css"
}
}Or change @reference to direct path:
@reference './styles/globals.css';Or use design-foundation directly:
@reference '@accelint/design-foundation/styles';Important Notes
- Tailwind defaults are removed - The design foundation resets Tailwind's default theme. Don't rely on any default values for colors, spacing, shadows, or typography. Only use what's explicitly defined in the design system.
- Semantic tokens are CSS variables - All semantic tokens compile to CSS custom properties like
var(--bg-surface-default). They can be used in custom CSS when utilities aren't sufficient, but prefer utilities. - Token fallback chain exists - Utility classes (
bg-*,fg-*,icon-*,outline-*) check semantic tokens first, then fall back todomain-*andprimitive-*tokens. This fallback is intentional for rare cases where designs exceed the design system, but semantic tokens should be the default choice for consistency and theming. - Spacing scale is semantic, not pixel-mapped - Don't think "m = 16px". Think "m = medium spacing for this context". The scale adapts to different contexts (button padding vs section margin). Numeric classes work with 1:1 relationship (p-1 = 1px, NOT 4px), but are fallbacks for non-conforming designs only.
- Theme variants use @ directives - The
@theme staticand@variant light/darksyntax is custom. Don't confuse with standard CSS media queries. Themes are applied via class/attribute on root element. - Design Toolkit components are React Aria - They use React Aria for accessibility. Don't fight the React Aria state management or variants. Work with
classNameandclassNamesprops. - Setup is NOT optional - The PostCSS plugin and @reference directives are required, not conveniences. Missing setup causes cryptic build errors ("undefined variable", "@variant not found"). Always verify setup before debugging styling issues.
Accelint Design Foundation
Note:
This document is mainly for agents and LLMs to follow when styling components with@accelint/design-foundationor@accelint/design-toolkit. Humans may also find it useful, but guidance here is optimized for automation and consistency by AI-assisted workflows.
---
Abstract
Quick-reference guide for styling with Accelint's opinionated Tailwind implementation. This guide provides token catalogs, spacing scales, and variant patterns organized for progressive disclosure. Each rule includes one-line summaries with links to detailed examples in the references/ folder.
Token efficiency principle: This guide assumes Claude knows standard Tailwind and CSS concepts. Focus is on what's different about Design Foundation's opinionated approach: semantic tokens vs raw colors, semantic spacing vs numeric, outlines vs borders, data attributes vs arbitrary variants.
---
How to Use This Guide
1. Start here: Scan the quick reference to identify relevant tokens and patterns 2. Load references as needed: Click through to detailed catalogs only when implementing 3. Progressive loading: Each reference file is self-contained with complete examples
This structure minimizes context usage while providing complete implementation guidance when needed.
---
Quick Reference
- 1.1 Color Tokens - Semantic tokens for backgrounds, text, icons, outlines
- 1.2 Spacing Scale - Seven-step semantic scale (xxs through xxl)
- 1.3 Variant System - Data attribute variants for component states
- 1.4 CSS Layer Structure - Layer hierarchy for custom component styles
- 1.5 Typography & Shadows - Text sizing and elevation scales
---
1. Core Token System
1.1 Color Tokens
Semantic tokens that adapt to light/dark themes automatically. Never use raw Tailwind colors or manual theme handling.
Token categories:
bg-* Backgrounds (surface-default, surface-raised, interactive-bold, info-muted, critical-muted)
fg-* Text (primary-bold, primary-muted, inverse-bold, accent-primary-bold, critical-bold)
icon-* Icons (inherits from fg-*, uses same token names)
outline-* Outlines/borders (interactive, static, info-bold, critical-bold, advisory-bold)
shadow-* Elevation (elevation-raised-muted, elevation-raised-bold, elevation-overlay-muted)Common patterns:
// Card
bg-surface-default outline-1 outline-interactive shadow-elevation-raised-muted
// Primary button
bg-interactive-bold fg-inverse-bold
// Body text
fg-primary-bold
// Link
fg-accent-primary-bold
// Alert
bg-info-muted outline-1 outline-info-boldView complete token catalog
1.2 Spacing Scale
Eight-step semantic scale (strongly preferred). Numeric classes available as fallback with 1:1 relationship (p-1 = 1px).
Scale: xxs → xs → s → m → l → xl → xxl → oversized
Classes:
- Padding:
p-m,px-s,py-xs,pt-l,p-oversized - Margin:
m-l,mx-m,my-s,mb-xl,m-oversized - Gap:
gap-m,gap-x-s,gap-y-l,gap-oversized - Space:
space-x-s,space-y-m,space-y-oversized
Common usage:
// ✅ Preferred - semantic scale
// Standard button: px-m py-xs
// Card padding: p-m
// Form field gaps: space-y-m
// Button group: gap-s
// ⚠️ Rare fallback - numeric (1:1 px)
// Non-conforming: p-13 (13px exactly)Numeric fallback: Design foundation implements --spacing: 1px; so p-1 = 1px (NOT 4px). Use only for non-conforming designs.
View spacing scale details
1.3 Variant System
Data attributes for component variants instead of arbitrary Tailwind variants.
Common variants:
data-color="info|success|warning|danger|primary|secondary"
data-size="small|medium|large"
data-state="active|inactive|disabled|loading|error"
data-layout="horizontal|vertical|grid|stack"
data-position="top|right|bottom|left|center"Usage:
<Button data-color="primary" data-size="large">Submit</Button>
<Alert data-color="warning" data-state="active">Warning</Alert>
<Badge data-color="success" data-size="small">New</Badge>React Aria states: Components also get data-hovered, data-pressed, data-focused, data-selected automatically.
View variant catalog
1.4 CSS Layer Structure
Custom CSS must use layer hierarchy for predictable specificity.
Layers:
@layer components.l1 { /* Base component styles */ }
@layer components.l2 { /* Variant styles */ }
@layer components.l3 { /* State styles */ }Lower numbers = lower specificity. Use l1 for base, l2 for variants, l3 for states.
1.5 Typography & Shadows
Typography scale:
text-body-xxs Tiny body text
text-body-xs Extra small body
text-body-s Small body text
text-body-m Medium body text (default)
text-body-l Large body text
text-header-xs Extra small headers
text-header-s Small headers
text-header-m Medium headers
text-header-l Large headers
text-header-xl Extra large headers
text-header-xxl Huge headers
text-button-xs Extra small button text
text-button-s Small button text
text-button-m Medium button text
text-button-l Large button textEach size includes coordinated font-size, line-height, letter-spacing, font-weight.
Shadow scale:
shadow-elevation-raised-muted Subtle raised elevation (cards)
shadow-elevation-raised-bold Prominent raised elevation
shadow-elevation-overlay-muted Subtle overlay elevation (popovers)
shadow-elevation-overlay-bold Prominent overlay elevation (modals)---
2. Key Patterns
Pattern: Outline vs Border
Use outlines, not borders. Outlines don't affect element dimensions, preventing layout shifts.
// ✅ Correct - outline doesn't add to size
<button className="outline-2 outline-interactive px-m py-xs">
Click
</button>
// ❌ Wrong - border adds to dimensions
<button className="border-2 border-blue-500 px-m py-xs">
Click
</button>Exception: Use borders only when the border must participate in layout (table cells, explicit separators).
Pattern: Automatic Theming
Never use manual theme handling. Semantic tokens adapt automatically.
// ✅ Correct - automatic theming
<div className="bg-surface-default fg-primary-bold">
Content
</div>
// ❌ Wrong - manual theme logic
<div className={theme === 'dark' ? 'bg-gray-900 text-white' : 'bg-white text-black'}>
Content
</div>Pattern: Semantic vs Primitive
Use semantic tokens in components, never primitives.
Primitives (--primitive-neutral-50) are for theme definitions only. Components use semantic tokens (bg-surface-default) that map to primitives.
Pattern: Overriding Design Toolkit Components
Use `className` or `classNames` props, not custom CSS files.
// ✅ Correct - use provided props
<DesignToolkitButton
className="custom-override"
classNames={{ icon: 'icon-override' }}
>
Action
</DesignToolkitButton>---
3. Migration from Vanilla Tailwind
Quick conversion:
| Vanilla Tailwind | Design Foundation |
|---|---|
bg-gray-100 | bg-surface-default |
text-gray-900 | fg-primary-bold |
p-4 | p-m |
gap-2 | gap-xs |
border-2 border-gray-300 | outline-2 outline-interactive |
dark:bg-gray-900 | Remove - automatic with tokens |
View complete migration guide
/**
* user-card.module.css
*
* Demonstrates correct patterns:
* - @layer for specificity control
* - Single @apply per rule (enables IDE plugin support)
* - @variant blocks for conditional styling (not attribute selectors)
* - Semantic tokens that adapt to theme automatically
*/
/* Base component styles */
@layer components.l1 {
.card {
@apply bg-surface-default outline-1 outline-interactive shadow-elevation-raised-muted p-m;
}
.header {
@apply flex items-center justify-between mb-s;
}
.title {
@apply fg-primary-bold text-body-l;
}
.content {
@apply space-y-xs mb-m;
}
.email {
@apply fg-primary-bold text-body-m;
}
.role {
@apply fg-primary-muted text-body-s;
}
.actions {
@apply flex gap-s;
}
.primaryButton,
.dangerButton {
@apply px-m py-xs;
}
.badge {
@apply px-s py-xxs text-body-s;
}
}
/* Variant styles - higher layer for proper cascade */
@layer components.l2 {
/* ✅ Correct - @variant blocks for size variants */
.card {
@variant size-small {
@apply p-s;
}
@variant size-large {
@apply p-l;
}
}
/* Button color variants using @variant blocks */
.primaryButton {
@variant color-info {
@apply bg-interactive-bold fg-inverse-bold;
}
}
.dangerButton {
@variant color-critical {
@apply outline-2 outline-critical-bold fg-critical-bold;
}
}
/* Button size variants */
.primaryButton,
.dangerButton {
@variant size-large {
@apply px-l py-s;
}
@variant size-small {
@apply px-s py-xxs;
}
}
/* Badge color variants using semantic status tokens */
.badge {
@variant color-normal {
@apply bg-normal-muted fg-primary-bold;
}
@variant color-critical {
@apply bg-critical-muted fg-inverse-bold;
}
@variant color-advisory {
@apply bg-advisory-muted fg-primary-bold;
}
}
}
/* State styles - highest layer */
@layer components.l3 {
/* React Aria provides data-hovered, data-pressed automatically */
.primaryButton,
.dangerButton {
@variant hover {
@apply opacity-90;
}
@variant pressed {
@apply opacity-80;
}
@variant disabled {
@apply opacity-50 cursor-not-allowed;
}
}
}
/**
* ANTI-PATTERNS TO AVOID:
*
* ❌ Wrong - multiple @apply directives (breaks IDE plugins):
* .card {
* @apply bg-surface-default;
* @apply outline-1 outline-interactive;
* @apply shadow-elevation-raised-muted;
* @apply p-m;
* }
*
* ✅ Correct - single @apply directive:
* .card {
* @apply bg-surface-default outline-1 outline-interactive shadow-elevation-raised-muted p-m;
* }
*
*
* ❌ Wrong - attribute selectors for variants:
* .card[data-size="small"] {
* @apply p-s;
* }
*
* ✅ Correct - @variant blocks:
* .card {
* @variant size-small {
* @apply p-s;
* }
* }
*
*
* ❌ Wrong - numeric Tailwind spacing:
* .card {
* @apply p-4 gap-2;
* }
*
* ✅ Correct - semantic spacing scale:
* .card {
* @apply p-m gap-xs;
* }
*
*
* ❌ Wrong - manual theme handling:
* .card {
* background: var(--light-bg);
* }
* [data-theme="dark"] .card {
* background: var(--dark-bg);
* }
*
* ✅ Correct - automatic with semantic tokens:
* .card {
* @apply bg-surface-default;
* }
*
*
* ❌ Wrong - borders add to dimensions:
* .card {
* @apply border-2 border-gray-300;
* }
*
* ✅ Correct - outlines don't affect size:
* .card {
* @apply outline-1 outline-interactive;
* }
*
*
* ❌ Wrong - no layers causes specificity issues:
* .card {
* @apply bg-surface-default;
* }
*
* ✅ Correct - use layer hierarchy:
* @layer components.l1 {
* .card {
* @apply bg-surface-default;
* }
* }
*/
/**
* Example: User Card Component
*
* Demonstrates correct @accelint/design-foundation patterns:
* - Component styles in CSS modules (not inline)
* - Semantic color tokens that adapt to theme
* - Semantic spacing scale (not numeric Tailwind)
* - Outlines instead of borders
* - Data attribute variants
* - CSS layer hierarchy
* - Inline classes ONLY for one-off overrides
*/
import { clsx } from '@accelint/design-foundation/lib/utils';
import styles from './example-component.module.css';
type UserCardProps = {
name: string;
email: string;
role: string;
status: 'active' | 'inactive' | 'pending';
size?: 'small' | 'medium' | 'large';
className?: string; // For one-off overrides
onEdit?: () => void;
onDelete?: () => void;
}
export function UserCard({
name,
email,
role,
status,
size = 'medium',
className,
onEdit,
onDelete
}: UserCardProps) {
return (
<article
className={clsx(styles.card, className)} // CSS module + optional override
data-size={size}
>
<header className={styles.header}>
<h3 className={styles.title}>{name}</h3>
<StatusBadge status={status} />
</header>
<div className={styles.content}>
<p className={styles.email}>{email}</p>
<p className={styles.role}>{role}</p>
</div>
{(onEdit || onDelete) && (
<footer className={styles.actions}>
{onEdit && (
<button
onClick={onEdit}
className={styles.primaryButton}
data-color="primary"
data-size={size}
>
Edit
</button>
)}
{onDelete && (
<button
onClick={onDelete}
className={styles.dangerButton}
data-color="danger"
data-size={size}
>
Delete
</button>
)}
</footer>
)}
</article>
);
}
type StatusBadgeProps = {
status: 'active' | 'inactive' | 'pending';
}
// Hoist static maps outside component to avoid re-creation on every render
const STATUS_COLOR_MAP = {
active: 'success',
inactive: 'danger',
pending: 'warning'
} as const;
const STATUS_LABEL_MAP = {
active: 'Active',
inactive: 'Inactive',
pending: 'Pending'
} as const;
function StatusBadge({ status }: StatusBadgeProps) {
return (
<span
className={styles.badge}
data-color={STATUS_COLOR_MAP[status]}
data-size="small"
>
{STATUS_LABEL_MAP[status]}
</span>
);
}
Accelint Design Foundation
Agent skill for styling with @accelint/design-foundation and @accelint/design-toolkit packages. Provides expert knowledge of opinionated Tailwind conventions including semantic tokens, custom spacing scale, outline-based borders, @variant system, and CSS module patterns.
Installation
npm
npx skills add https://github.com/gohypergiant/agent-skills --skill accelint-design-foundationpnpm
pnpm dlx skills add https://github.com/gohypergiant/agent-skills --skill accelint-design-foundationUsage
This skill automatically activates when working with components that use @accelint/design-foundation or @accelint/design-toolkit.
Example prompts that trigger the skill:
Style this Button component using Design Foundation tokens
Add styling to this card with the design system
Theme this component for light and dark mode
Update the spacing in this component to use the semantic scaleWhat's Included
- SKILL.md - Core patterns, anti-patterns, and styling workflows
- AGENTS.md - Quick reference for tokens, spacing, and variants
- references/ - Detailed catalogs and migration guide:
token-reference.md- Complete token catalog with examplesvariant-system.md- @variant system usage patternsspacing-scale.md- Semantic spacing scale guidemigration-guide.md- Converting from vanilla Tailwind- assets/ - Example component showing correct patterns
Key Concepts
CSS Modules First
Component styles belong in CSS modules (.module.css), not inline className props. Inline Tailwind classes should only be used for minor one-off overrides.
Semantic Tokens
Use semantic tokens like bg-surface-default and fg-primary-bold instead of raw Tailwind colors. Semantic tokens automatically adapt to light/dark themes.
Semantic Spacing
Use the seven-step semantic scale (xxs → xs → s → m → l → xl → xxl) instead of numeric Tailwind spacing (p-4, gap-2).
Outlines Over Borders
Prefer outline classes over border classes. Outlines don't affect element dimensions, preventing layout shifts.
@variant System
Use @variant directive blocks in CSS modules for conditional styling based on data attributes.
Examples
Basic Card Component
Card.tsx
import styles from './Card.module.css';
export function Card({ children, size = 'medium' }) {
return (
<div className={styles.card} data-size={size}>
{children}
</div>
);
}Card.module.css
@layer components.l1 {
.card {
@apply bg-surface-default outline-1 outline-interactive shadow-elevation-raised-muted p-m;
}
}
@layer components.l2 {
.card {
@variant size-large {
@apply p-l;
}
@variant size-small {
@apply p-s;
}
}
}Requirements
@accelint/design-foundationpackage installed- Tailwind CSS configured to use design foundation
- CSS modules support in build system
Learn More
- AGENTS.md - Quick reference guide
- SKILL.md - Complete styling patterns
- references/ - Detailed catalogs and guides
License
Apache-2.0
Migration Guide
How to convert vanilla Tailwind styling to @accelint/design-foundation conventions.
Core Differences
| Aspect | Vanilla Tailwind | Design Foundation |
|---|---|---|
| Colors | bg-gray-100, text-blue-500 | bg-surface-default, fg-primary-bold |
| Spacing | p-4, gap-6, m-8 | p-m, gap-l, m-xl |
| Borders | border-2 border-gray-300 | outline-2 outline-interactive |
| Themes | dark:bg-gray-900 | Automatic with semantic tokens |
| Variants | Arbitrary hover:[&>svg]:opacity-50 | Data attributes data-color="info" |
Step-by-Step Migration
Step 1: Replace Color Classes
Before (Vanilla Tailwind):
<div className="bg-white text-gray-900 border-gray-200">
<h2 className="text-blue-600">Heading</h2>
<p className="text-gray-600">Body text</p>
</div>After (Design Foundation):
<div className="bg-surface-default fg-primary-bold outline-1 outline-interactive">
<h2 className="fg-accent-primary-bold">Heading</h2>
<p className="fg-primary-bold">Body text</p>
</div>Strategy: 1. bg-white/bg-gray-* → bg-surface-* (default, secondary, tertiary) 2. text-gray-* → fg-primary-* (bold, default, subtle) 3. text-blue-* (interactive) → fg-accent-primary-bold 4. border-* → outline-*
Step 2: Replace Spacing Classes
Before (Vanilla Tailwind):
<button className="px-6 py-2">
Click
</button>
<div className="p-4 gap-2">
<Card />
<Card />
</div>After (Design Foundation):
<button className="px-m py-xs">
Click
</button>
<div className="p-m gap-xs">
<Card />
<Card />
</div>Mapping:
p-0.5→p-xxsp-1→p-xsp-2→p-sp-3→p-mp-4→p-lp-6→p-xlp-10→p-xxl
Step 3: Replace Dark Mode Handling
Before (Vanilla Tailwind):
<div className="bg-white dark:bg-gray-900 text-black dark:text-white">
Content
</div>
// Or with manual theme
<div className={theme === 'dark' ? 'bg-gray-900 text-white' : 'bg-white text-black'}>
Content
</div>After (Design Foundation):
<div className="bg-surface-default fg-primary-bold">
Content
</div>
// Automatically adapts to light/dark themeNo manual theme handling needed. Semantic tokens adapt automatically.
Step 4: Replace Border with Outline
Before (Vanilla Tailwind):
<button className="border-2 border-blue-500 hover:border-blue-700">
Action
</button>
<input className="border border-gray-300 focus:border-blue-500" />After (Design Foundation):
<button className="outline-2 outline-interactive hover:outline-interactive">
Action
</button>
<input className="outline-1 outline-interactive focus:outline-interactive" />Why: Outlines don't affect element dimensions, making layouts more predictable.
Step 5: Replace Arbitrary Variants with Data Attributes
Before (Vanilla Tailwind):
<Button className="hover:[&>svg]:opacity-50 data-[state=active]:bg-blue-600">
<Icon />
Action
</Button>After (Design Foundation):
<Button data-state="active" data-color="primary">
<Icon />
Action
</Button>Or with conditional classes:
<Button className={clsx(isActive && 'bg-surface-raised')}>
<Icon className={clsx(isHovered && 'opacity-50')} />
Action
</Button>Step 6: Remove Default Tailwind Assumptions
Before (Vanilla Tailwind):
// Relying on default shadow-md, default colors
<Card className="shadow-md">
Content
</Card>After (Design Foundation):
// Explicitly use design system shadows and colors
<Card className="shadow-elevation-raised-muted bg-surface-default">
Content
</Card>Why: Design Foundation removes Tailwind defaults. Always specify explicitly.
Common Component Migrations
Button Component
Before:
<button className="bg-blue-500 hover:bg-blue-700 text-white px-6 py-2 rounded">
Primary
</button>
<button className="border-2 border-blue-500 text-blue-500 hover:bg-blue-50 px-6 py-2 rounded">
Secondary
</button>After:
<button className="bg-interactive-bold fg-inverse-bold px-m py-xs" data-color="primary">
Primary
</button>
<button className="outline-2 outline-interactive fg-accent-primary-bold px-m py-xs" data-color="secondary">
Secondary
</button>Card Component
Before:
<div className="bg-white border border-gray-200 rounded-lg p-6 shadow-md">
<h3 className="text-xl font-bold text-gray-900">Title</h3>
<p className="text-gray-600">Content</p>
</div>After:
<div className="bg-surface-default outline-1 outline-interactive p-m shadow-elevation-raised-muted">
<h3 className="text-body-l font-bold fg-primary-bold">Title</h3>
<p className="fg-primary-bold">Content</p>
</div>Form Input
Before:
<input
className="border border-gray-300 focus:border-blue-500 focus:ring-blue-500 px-4 py-2 rounded"
placeholder="Enter text"
/>After:
<input
className="outline-1 outline-interactive focus:outline-interactive px-s py-xs"
placeholder="Enter text"
/>Alert/Notification
Before:
<div className="bg-blue-50 border-l-4 border-blue-500 p-4">
<div className="flex">
<InfoIcon className="text-blue-500" />
<p className="text-blue-700">Info message</p>
</div>
</div>After:
<div className="bg-info-muted outline-l-4 outline-info-bold p-m" data-color="info">
<div className="flex gap-s">
<InfoIcon className="icon-primary-default" />
<p className="fg-primary-bold">Info message</p>
</div>
</div>Navigation
Before:
<nav className="bg-white border-b border-gray-200">
<div className="flex gap-4 px-6 py-3">
<a className="text-blue-600 hover:text-blue-800">Home</a>
<a className="text-gray-600 hover:text-gray-900">About</a>
</div>
</nav>After:
<nav className="bg-surface-default outline-b-1 outline-interactive">
<div className="flex gap-m px-m py-s">
<a className="fg-accent-primary-bold hover:fg-primary-bold">Home</a>
<a className="fg-primary-bold hover:fg-primary-bold">About</a>
</div>
</nav>Typography Migration
Before:
<h1 className="text-4xl font-bold text-gray-900">Heading</h1>
<p className="text-base text-gray-600">Body text</p>
<span className="text-sm text-gray-500">Caption</span>After:
<h1 className="text-header-xxl font-bold fg-primary-bold">Heading</h1>
<p className="text-body-m fg-primary-bold">Body text</p>
<span className="text-body-s fg-primary-muted">Caption</span>Font size mapping:
text-body-xs→text-body-xs(same)text-sm→text-body-stext-base→text-body-mtext-lg→text-body-ltext-header-xlthroughtext-4xl→text-header-xlortext-header-xxl
Shadow Migration
Before:
<div className="shadow">Default</div>
<div className="shadow-elevation-raised-mutedd">Medium</div>
<div className="shadow-lg">Large</div>
<div className="shadow-elevation-overlay-bold">Extra Large</div>After:
<div className="shadow-elevation-raised-muted">Small</div>
<div className="shadow-elevation-raised-muted">Medium</div>
<div className="shadow-elevation-overlay-muted">Large</div>
<div className="shadow-elevation-overlay-bold">Extra Large</div>Migration Checklist
- [ ] Replace all color classes with semantic tokens
- [ ] Replace numeric spacing with semantic scale
- [ ] Remove manual dark mode handling
- [ ] Replace borders with outlines (where appropriate)
- [ ] Replace arbitrary variants with data attributes
- [ ] Explicitly specify all styling (no Tailwind defaults)
- [ ] Update typography classes to design system scale
- [ ] Test in both light and dark themes
- [ ] Verify responsive spacing works correctly
- [ ] Check that outlines don't break layouts
Testing After Migration
// Test both themes
<div data-theme="light">
{/* Component should look correct */}
</div>
<div data-theme="dark">
{/* Component should look correct */}
</div>
// Test all states
<Button data-state="default">Default</Button>
<Button data-state="hover">Hover</Button>
<Button data-state="active">Active</Button>
<Button data-state="disabled">Disabled</Button>
// Test all color variants
<Alert data-color="info">Info</Alert>
<Alert data-color="success">Success</Alert>
<Alert data-color="warning">Warning</Alert>
<Alert data-color="danger">Danger</Alert>Common Pitfalls
❌ Keeping Numeric Spacing
// ❌ Wrong - still using numeric
<div className="p-4 gap-2">Content</div>
// ✅ Correct - semantic scale
<div className="p-m gap-xs">Content</div>❌ Using Tailwind Color Classes
// ❌ Wrong - Tailwind colors don't exist
<div className="bg-gray-100 text-gray-800">Content</div>
// ✅ Correct - semantic tokens
<div className="bg-surface-default fg-primary-bold">Content</div>❌ Manual Theme Switching
// ❌ Wrong - manual theme logic
<div className={theme === 'dark' ? 'bg-black' : 'bg-white'}>
Content
</div>
// ✅ Correct - automatic with semantic tokens
<div className="bg-surface-default">Content</div>❌ Assuming Tailwind Defaults
// ❌ Wrong - assuming default shadow exists
<Card className="shadow">Content</Card>
// ✅ Correct - explicit design system shadow
<Card className="shadow-elevation-raised-muted">Content</Card>Setup Guide
Complete setup requirements for @accelint/design-foundation to work correctly.
Required Configuration
1. PostCSS Configuration (Required)
Create `postcss.config.mjs` in project root:
export default {
plugins: {
'@tailwindcss/postcss': {},
'@accelint/postcss-tailwind-css-modules': {},
},
};What it does: The @accelint/postcss-tailwind-css-modules plugin fixes named group selectors (like group-hover/button:) and @variant selectors in CSS module files.
Without it: Named groups and @variant selectors fail to resolve, causing build errors or non-functional styles.
Example of what breaks without the plugin:
/* This won't work without the plugin */
.container {
@variant group-hover/button {
@apply bg-surface-hover;
}
}
/* This also won't work */
.item {
@apply group-hover/button:opacity-50;
}2. CSS Module @reference Directive (Required)
Every CSS module file must include a @reference directive at the top:
Option A - If project has custom CSS entrypoint:
@reference '#globals';
@layer components.l1 {
.button {
@apply px-m py-xs bg-interactive-bold;
}
}Option B - If no custom entrypoint, reference design-foundation directly:
@reference '@accelint/design-foundation/styles';
@layer components.l1 {
.button {
@apply px-m py-xs bg-interactive-bold;
}
}What it does: Imports design system tokens, utilities, and variant definitions into the CSS module scope.
Without it: Semantic tokens (bg-surface-default), @variant blocks, and @layer directives are undefined, causing "unknown variable" errors.
3. Package.json Imports (Optional - If Custom Entrypoint)
If the project implements a custom CSS entrypoint for configuring tokens/utilities:
{
"imports": {
"#globals": "./src/styles/globals.css"
}
}What it does: Allows CSS modules to reference the custom entrypoint via @reference '#globals'; instead of typing the full path.
When to use:
- Project needs custom token overrides
- Project adds custom utilities beyond design-foundation
- Project configures theme-specific values
When to skip:
- Using design-foundation as-is without customization
- Reference
'@accelint/design-foundation/styles'directly instead
4. Custom CSS Entrypoint (Optional)
If implementing custom tokens/utilities, create a CSS entrypoint:
`src/styles/globals.css`:
/* Import design foundation base */
@import "@accelint/design-foundation/styles";
/* Add custom token overrides */
@theme {
--custom-brand-primary: #ff0000;
--custom-brand-secondary: #00ff00;
}
/* Add custom utilities */
@utility custom-gradient {
background: linear-gradient(to right, var(--custom-brand-primary), var(--custom-brand-secondary));
}Then import as FIRST import in root layout:
import './styles/globals.css'; // Must be first
import { ReactNode } from 'react';
export default function RootLayout({ children }: { children: ReactNode }) {
return <html lang="en">{children}</html>;
}Why first: Design system styles must load before component styles for correct cascade.
Setup Verification Checklist
[ ] PostCSS config exists with @accelint/postcss-tailwind-css-modules plugin
[ ] Every .module.css file has @reference directive at the top
[ ] If custom entrypoint: package.json has imports field pointing to it
[ ] If custom entrypoint: globals.css imports @accelint/design-foundation/styles
[ ] CSS entrypoint (custom or design-foundation) imported first in root layoutCommon Setup Issues
Issue: "undefined variable --bg-surface-default"
Cause: Missing @reference directive in CSS module.
Fix: Add @reference '#globals'; or @reference '@accelint/design-foundation/styles'; at the top of the CSS module file.
/* ✅ Correct */
@reference '@accelint/design-foundation/styles';
@layer components.l1 {
.card {
@apply bg-surface-default; /* Now defined */
}
}Issue: "@variant directive not recognized"
Cause: Missing @reference directive or PostCSS plugin not configured.
Fix 1: Verify @reference directive exists at top of CSS module.
Fix 2: Check postcss.config.mjs includes @accelint/postcss-tailwind-css-modules plugin.
Issue: "group-hover/button: selector not working"
Cause: Missing @accelint/postcss-tailwind-css-modules plugin.
Fix: Add plugin to postcss.config.mjs:
export default {
plugins: {
'@tailwindcss/postcss': {},
'@accelint/postcss-tailwind-css-modules': {}, // Add this
},
};Issue: Styles not applying / tokens showing as raw values
Cause: CSS entrypoint not imported or imported too late.
Fix: Ensure globals.css (or @accelint/design-foundation/styles) is the FIRST import in root layout:
// ✅ Correct order
import './styles/globals.css'; // First
import './other-styles.css';
import Component from './Component';
// ❌ Wrong order
import Component from './Component';
import './styles/globals.css'; // Too lateIssue: "Cannot find module '#globals'"
Cause: Missing package.json imports field or path incorrect.
Fix: Add imports to package.json:
{
"imports": {
"#globals": "./src/styles/globals.css"
}
}Or: Change @reference to direct path:
@reference './styles/globals.css';Or: Use design-foundation directly:
@reference '@accelint/design-foundation/styles';Architecture Decision: Custom Entrypoint vs Direct Import
Use Custom Entrypoint When:
- Adding project-specific token overrides
- Implementing custom utilities beyond design-foundation
- Configuring brand-specific theme values
- Need centralized place for design system extensions
Setup:
// package.json
{ "imports": { "#globals": "./src/styles/globals.css" } }/* CSS modules */
@reference '#globals';Use Direct Import When:
- Using design-foundation as-is without customization
- No need for project-specific tokens or utilities
- Simpler setup with fewer files
Setup:
/* CSS modules */
@reference '@accelint/design-foundation/styles';Examples
Minimal Setup (No Custom Entrypoint)
postcss.config.mjs:
export default {
plugins: {
'@tailwindcss/postcss': {},
'@accelint/postcss-tailwind-css-modules': {},
},
};Button.module.css:
@reference '@accelint/design-foundation/styles';
@layer components.l1 {
.button {
@apply px-m py-xs bg-interactive-bold fg-inverse-bold;
}
}layout.tsx:
import '@accelint/design-foundation/styles'; // First import
import { ReactNode } from 'react';
export default function RootLayout({ children }: { children: ReactNode }) {
return <html>{children}</html>;
}Full Setup (With Custom Entrypoint)
postcss.config.mjs:
export default {
plugins: {
'@tailwindcss/postcss': {},
'@accelint/postcss-tailwind-css-modules': {},
},
};package.json:
{
"imports": {
"#globals": "./src/styles/globals.css"
}
}src/styles/globals.css:
@import "@accelint/design-foundation/styles";
@theme {
--custom-brand-red: #ff0000;
}Button.module.css:
@reference '#globals';
@layer components.l1 {
.button {
@apply px-m py-xs bg-interactive-bold fg-inverse-bold;
}
.buttonBrand {
background: var(--custom-brand-red);
}
}layout.tsx:
import './styles/globals.css'; // First import
import { ReactNode } from 'react';
export default function RootLayout({ children }: { children: ReactNode }) {
return <html>{children}</html>;
}Anti-Patterns
❌ Missing @reference Directive
/* ❌ Wrong - no @reference */
@layer components.l1 {
.button {
@apply bg-surface-default; /* Error: undefined */
}
}
/* ✅ Correct - has @reference */
@reference '@accelint/design-foundation/styles';
@layer components.l1 {
.button {
@apply bg-surface-default; /* Works */
}
}❌ Wrong @reference Location
/* ❌ Wrong - @reference not at top */
@layer components.l1 {
@reference '#globals';
.button { /* ... */ }
}
/* ✅ Correct - @reference at top */
@reference '#globals';
@layer components.l1 {
.button { /* ... */ }
}❌ Importing Styles Too Late
// ❌ Wrong - imports after components
import Button from './components/Button';
import Card from './components/Card';
import './styles/globals.css'; // Too late!
// ✅ Correct - styles first
import './styles/globals.css'; // First
import Button from './components/Button';
import Card from './components/Card';❌ Missing PostCSS Plugin
// ❌ Wrong - missing CSS modules plugin
export default {
plugins: {
'@tailwindcss/postcss': {},
},
};
// ✅ Correct - includes both plugins
export default {
plugins: {
'@tailwindcss/postcss': {},
'@accelint/postcss-tailwind-css-modules': {},
},
};Spacing Scale
Semantic spacing scale in @accelint/design-foundation with numeric fallback for edge cases.
The Scale
xxs → xs → s → m → l → xl → xxl → oversizedEight steps from extra-extra-small to oversized.
Key Principle
Think semantic meaning, not pixel values.
The spacing scale is contextual. m means "medium for this context", not a specific pixel value. A medium gap between buttons is different from medium padding inside a card.
Spacing System Architecture
Strong preference: Semantic scale
- Use
p-m,gap-s,mb-lfor design system consistency - Provides contextual, meaningful spacing that adapts to use case
Fallback: Numeric classes (rare)
- Design foundation implements
--spacing: 1px;for numeric classes p-1= 1px (NOT 4px like vanilla Tailwind)p-12= 12px (1:1 relationship)- Use ONLY for non-conforming designs where semantic scale doesn't fit
The choice:
/* ✅ Preferred - semantic scale */
.card {
@apply p-m space-y-s;
}
/* ⚠️ Acceptable but rare - numeric fallback */
.nonStandardLayout {
@apply p-13; /* 13px exactly - edge case only */
}
/* ❌ Wrong - using numeric when semantic exists */
.button {
@apply px-16 py-8; /* Should use px-m py-xs */
}Spacing Classes
Padding
p-xxs All sides, extra extra small
p-xs All sides, extra small
p-s All sides, small
p-m All sides, medium (common default)
p-l All sides, large
p-xl All sides, extra large
p-xxl All sides, extra extra large
p-oversized All sides, oversized
px-[size] Horizontal padding (left + right)
py-[size] Vertical padding (top + bottom)
pt-[size] Top padding
pr-[size] Right padding
pb-[size] Bottom padding
pl-[size] Left paddingMargin
m-xxs All sides, extra extra small
m-xs All sides, extra small
m-s All sides, small
m-m All sides, medium
m-l All sides, large
m-xl All sides, extra large
m-xxl All sides, extra extra large
m-oversized All sides, oversized
mx-[size] Horizontal margin (left + right)
my-[size] Vertical margin (top + bottom)
mt-[size] Top margin
mr-[size] Right margin
mb-[size] Bottom margin
ml-[size] Left marginGap (Flexbox/Grid)
gap-xxs Gap between flex/grid children, extra extra small
gap-xs Gap between flex/grid children, extra small
gap-s Gap between flex/grid children, small
gap-m Gap between flex/grid children, medium
gap-l Gap between flex/grid children, large
gap-xl Gap between flex/grid children, extra large
gap-xxl Gap between flex/grid children, extra extra large
gap-oversized Gap between flex/grid children, oversized
gap-x-[size] Horizontal gap (column gap)
gap-y-[size] Vertical gap (row gap)Space Between
space-x-xxs Horizontal space between children
space-x-xs
space-x-s
space-x-m
space-x-l
space-x-xl
space-x-xxl
space-x-oversized
space-y-xxs Vertical space between children
space-y-xs
space-y-s
space-y-m
space-y-l
space-y-xl
space-y-xxl
space-y-oversizedUsage Patterns
Button Padding
// Compact button
<button className="px-s py-xxs">
Small
</button>
// Standard button
<button className="px-m py-xs">
Default
</button>
// Large button
<button className="px-l py-s">
Large
</button>Card Layout
// Card with consistent spacing
<div className="p-m space-y-s">
<h2>Heading</h2>
<p>Content paragraph</p>
<button>Action</button>
</div>
// Nested card with tighter spacing
<div className="p-l">
<div className="p-s bg-surface-secondary">
Nested content
</div>
</div>Form Spacing
// Form with semantic spacing
<form className="space-y-m">
<div className="space-y-xs">
<label>Email</label>
<input className="px-s py-xs" />
</div>
<div className="space-y-xs">
<label>Password</label>
<input className="px-s py-xs" />
</div>
<button className="px-m py-s mt-l">
Submit
</button>
</form>List Spacing
// List with items
<ul className="space-y-s">
<li className="p-xs">Item 1</li>
<li className="p-xs">Item 2</li>
<li className="p-xs">Item 3</li>
</ul>
// Horizontal list (navigation)
<nav className="flex gap-m">
<a className="px-s py-xs">Home</a>
<a className="px-s py-xs">About</a>
<a className="px-s py-xs">Contact</a>
</nav>Grid Layout
// Grid with consistent gaps
<div className="grid grid-cols-3 gap-m">
<Card className="p-m" />
<Card className="p-m" />
<Card className="p-m" />
</div>
// Tight grid
<div className="grid grid-cols-4 gap-xs">
<Thumbnail />
<Thumbnail />
<Thumbnail />
<Thumbnail />
</div>Scale Selection Guide
When to Use Each Size
xxs - Minimal separation
- Icon padding in small buttons
- Tight list item spacing
- Badge padding
xs - Compact spacing
- Button vertical padding
- Small card padding
- Form field internal spacing
s - Small but comfortable
- Button horizontal padding
- List item spacing
- Section gaps in compact layouts
m - Default spacing (most common)
- Card padding
- Form field gaps
- Standard component spacing
l - Generous spacing
- Large card padding
- Section separation
- Page margins
xl - Large spacing
- Hero section padding
- Major page sections
- Prominent component separation
xxl - Extra large spacing
- Page-level margins
- Major layout sections
- Landing page hero spacing
oversized - Maximum spacing
- Exceptional large spacing needs
- Dramatic section separation
- Special layout requirements
Migration from Numeric Scale
Strongly prefer semantic equivalents. Numeric classes work with 1:1 relationship (p-1 = 1px) but should only be used for non-conforming designs.
Vanilla Tailwind → Design Foundation (Semantic - Preferred)
p-1 → p-xxs (vanilla 4px → use semantic)
p-2 → p-xs (vanilla 8px → use semantic)
p-3 → p-s (vanilla 12px → use semantic)
p-4 → p-m (vanilla 16px → use semantic)
p-6 → p-l (vanilla 24px → use semantic)
p-8 → p-xl (vanilla 32px → use semantic)
p-12 → p-xxl (vanilla 48px → use semantic)
gap-2 → gap-xs
gap-4 → gap-m
gap-6 → gap-l
m-4 → m-m
m-8 → m-l
m-12 → m-xlNumeric Classes in Design Foundation (1:1 Relationship - Rare Fallback)
Design foundation implements `--spacing: 1px;` which changes how numeric classes work:
/* In design foundation (NOT vanilla Tailwind): */
p-1 = 1px (not 4px)
p-4 = 4px (not 16px)
p-12 = 12px (not 48px)
p-13 = 13px (not 52px)Use numeric classes ONLY for non-conforming designs:
// ✅ Preferred - semantic scale
<div className="p-m">Content</div>
// ⚠️ Acceptable but rare - exact pixel needed for edge case
<div className="p-13">
{/* 13px exactly - design doesn't fit semantic scale */}
</div>
// ❌ Wrong - using numeric when semantic exists
<div className="p-16">
{/* Should use p-m */}
</div>Important: Don't think in pixels with semantic scale. The semantic scale may map to different values in different contexts or future versions.
Combining Spacing
// ✅ Correct - consistent scale usage
<div className="p-m space-y-s">
<section className="mb-l">
<h2 className="mb-s">Section</h2>
<p>Content</p>
</section>
</div>
// ❌ Wrong - mixing semantic and numeric
<div className="p-4 space-y-s">
<section className="mb-6">
<h2 className="mb-2">Section</h2>
<p>Content</p>
</section>
</div>Anti-Patterns
❌ Don't Use Numeric Classes as First Choice
// ❌ Wrong - using numeric when semantic exists
<button className="px-16 py-8">Click</button>
// ✅ Correct - semantic scale
<button className="px-m py-xs">Click</button>
// ⚠️ Acceptable but rare - numeric for non-conforming design
<button className="px-17 py-9">
{/* 17px/9px exactly needed for edge case */}
Click
</button>❌ Don't Create Arbitrary Spacing Values
// ❌ Wrong - arbitrary value (not supported)
<div className="p-[13px]">Content</div>
// ✅ Correct - use closest semantic value
<div className="p-s">Content</div>
// ⚠️ Acceptable alternative - numeric class for exact value
<div className="p-13">
{/* 13px exactly (1:1 relationship) - for edge cases only */}
Content
</div>❌ Don't Mix Semantic and Numeric Scales
// ❌ Wrong - mixing semantic and numeric inconsistently
<div className="p-m gap-16">
{/* Inconsistent - use all semantic or all numeric (if needed) */}
Content
</div>
// ✅ Correct - consistent semantic scale (preferred)
<div className="p-m gap-m">
Content
</div>
// ⚠️ Acceptable if necessary - all numeric for edge case
<div className="p-16 gap-16">
{/* All numeric for non-conforming design - rare */}
Content
</div>Responsive Spacing
Apply different spacing at breakpoints:
// Responsive padding
<div className="p-s md:p-m lg:p-l">
Content adapts to viewport
</div>
// Responsive gap
<div className="flex gap-xs md:gap-s lg:gap-m">
<Card />
<Card />
</div>Breakpoints follow Tailwind conventions: sm:, md:, lg:, xl:, 2xl:
Token Reference
Complete catalog of design tokens in @accelint/design-foundation.
Token Architecture
Three-tier system with fallback chain: 1. Semantic tokens - Contextual names (e.g., bg-surface-default, fg-primary-bold) - STRONGLY PREFERRED 2. Domain tokens - Domain-specific values (e.g., domain-brand) - fallback for edge cases 3. Primitive tokens - Raw color values (e.g., primitive-neutral-50) - fallback for edge cases
/* Primitive - building block */
--primitive-neutral-50: #ffffff;
/* Semantic - contextual usage */
--bg-surface-default: var(--primitive-neutral-100, #eff1f2); /* light */
--bg-surface-default: var(--primitive-neutral-900, #151517); /* dark */Utility fallback chain:
/* bg-*, fg-*, icon-*, outline-* utilities check in order: */
--bg-value: --value(--bg-*, --domain-*, --primitive-*);
/* 1. Semantic token (--bg-surface-default) - use first
2. Domain token (--domain-brand) - fallback
3. Primitive token (--primitive-blue-500) - last resort */Strongly prefer semantic tokens - Use domain-* and primitive-* only for rare cases where design exceeds the design system. Semantic tokens provide theming and consistency.
Color Token Categories
Background Tokens (bg-*)
Surface and interactive backgrounds:
bg-surface-default Primary surface color (page/card background)
bg-surface-secondary Secondary surface (nested panels)
bg-surface-tertiary Tertiary surface (deepest nesting)
bg-surface-inverse Inverted surface (tooltips, high contrast)
bg-surface-raised Raised/elevated surface
bg-surface-hover Hover state background
bg-interactive-bold Primary action background
bg-interactive-muted Secondary action background
bg-info-muted Info message background
bg-normal-muted Success message background
bg-advisory-muted Warning message background
bg-critical-muted Danger/error message backgroundForeground Tokens (fg-*)
Text and content colors:
fg-primary-bold Primary text (headlines, body text)
fg-primary-muted Subtle text (captions, metadata, secondary content)
fg-inverse-bold Inverted text (on dark backgrounds)
fg-accent-primary-bold Interactive text/links
fg-info-bold Info message text
fg-normal-bold Success message text
fg-advisory-bold Warning message text
fg-critical-bold Danger/error message textIcon Tokens (icon-*)
Icon colors inherit from foreground tokens (same token names):
icon-primary-bold Primary icon emphasis
icon-primary-muted Subtle icon color
icon-inverse-bold Inverted icon (on dark backgrounds)
icon-accent-primary-bold Interactive icon color
icon-critical-bold Error/danger icon colorNote: The fg-* utility classes automatically set --icon-color, so icons inherit text colors.
Outline Tokens (outline-*)
Outline/border colors:
outline-interactive Interactive outlines (buttons, inputs)
outline-static Static outlines (borders, dividers)
outline-info-bold Info state outline
outline-normal-bold Success state outline
outline-advisory-bold Warning state outline
outline-critical-bold Danger state outlineShadow Tokens (shadow-*)
Elevation shadows:
shadow-elevation-raised-muted Subtle raised elevation (cards)
shadow-elevation-raised-bold Prominent raised elevation
shadow-elevation-overlay-muted Subtle overlay elevation (popovers)
shadow-elevation-overlay-bold Prominent overlay elevation (modals)Typography Tokens
Font Size Classes
text-body-xxs Tiny body text
text-body-xs Extra small body text
text-body-s Small body text
text-body-m Medium body text (default)
text-body-l Large body text
text-header-xs Extra small headers
text-header-s Small headers
text-header-m Medium headers
text-header-l Large headers
text-header-xl Extra large headers
text-header-xxl Huge headers
text-button-xs Extra small button text
text-button-s Small button text
text-button-m Medium button text
text-button-l Large button textEach size includes coordinated font-size, line-height, letter-spacing, and font-weight.
Font Weight
font-regular Regular weight (400)
font-medium Medium weight (500)
font-semibold Semibold weight (600)
font-bold Bold weight (700)Usage Examples
Semantic Token Selection
Card component:
<div className="bg-surface-default outline-1 outline-interactive shadow-elevation-raised-muted">
<h2 className="fg-primary-bold text-body-l">Heading</h2>
<p className="fg-primary-bold text-body-m">Body text</p>
<span className="fg-primary-muted text-body-s">Metadata</span>
</div>Button variants:
// Primary button
<button className="bg-interactive-bold fg-inverse-bold">
Primary Action
</button>
// Secondary button
<button className="bg-interactive-muted fg-accent-primary-bold">
Secondary Action
</button>
// Danger button
<button className="bg-critical-muted fg-inverse-bold">
Delete
</button>Status messages:
<div className="bg-info-muted outline-1 outline-info-bold">
<Icon className="icon-primary-default" />
<span className="fg-primary-bold">Info message</span>
</div>Theme Adaptation
All semantic tokens automatically adapt when theme changes:
// No manual theme handling needed
<div className="bg-surface-default fg-primary-bold">
{/* Automatically light in light theme, dark in dark theme */}
</div>
// ❌ Don't do this
<div className={theme === 'dark' ? 'bg-gray-900 text-white' : 'bg-white text-black'}>
{/* Manual theme handling breaks the system */}
</div>Token Lookup Pattern
When choosing a token:
1. Identify element purpose - Is this a surface, text, icon, or outline? 2. Determine hierarchy - Primary, secondary, or tertiary emphasis? 3. Consider state - Default, hover, active, disabled? 4. Check status - Info, success, warning, danger?
Example: "I need text color for a secondary heading" → Purpose: text (fg-*) → Hierarchy: secondary with emphasis (secondary-bold) → Result: fg-primary-muted
Example: "I need background for a hover state on a surface" → Purpose: background (bg-*) → State: hover on surface (surface-hover) → Result: bg-surface-hover
Common Issues and Solutions
This reference provides solutions to frequently encountered issues when working with @accelint/design-foundation.
Issue: "undefined variable --bg-surface-default"
Cause: Missing @reference directive in CSS module.
Symptom: Build error when using semantic tokens like bg-surface-default, fg-primary-bold, etc.
Fix: Add @reference '#globals'; or @reference '@accelint/design-foundation/styles'; at the top of the CSS module file.
/* ✅ Correct */
@reference '@accelint/design-foundation/styles';
@layer components.l1 {
.card {
@apply bg-surface-default; /* Now defined */
}
}---
Issue: "@variant directive not recognized"
Cause: Missing @reference directive or PostCSS plugin not configured.
Symptom: Build error when using @variant blocks like @variant color-info { ... }
Fix 1: Verify @reference directive exists at top of CSS module.
Fix 2: Check postcss.config.mjs includes @accelint/postcss-tailwind-css-modules plugin:
export default {
plugins: {
'@tailwindcss/postcss': {},
'@accelint/postcss-tailwind-css-modules': {}, // Add this
},
};---
Issue: "group-hover/button: selector not working"
Cause: Missing @accelint/postcss-tailwind-css-modules plugin.
Symptom: Named group selectors like group-hover/button: don't apply styles in CSS modules.
Fix: Add plugin to postcss.config.mjs:
export default {
plugins: {
'@tailwindcss/postcss': {},
'@accelint/postcss-tailwind-css-modules': {}, // Required for named groups
},
};---
Issue: Styles not applying / tokens showing as raw values
Cause: CSS entrypoint not imported or imported too late in component tree.
Symptom: Semantic tokens render as literal var(--bg-surface-default) instead of colors, or styles don't apply at all.
Fix: Ensure globals.css (or @accelint/design-foundation/styles) is the FIRST import in root layout:
// ✅ Correct order
import './styles/globals.css'; // First
import './other-styles.css';
import Component from './Component';
// ❌ Wrong order
import Component from './Component';
import './styles/globals.css'; // Too late---
Issue: "Cannot find module '#globals'"
Cause: Missing package.json imports field or path incorrect.
Symptom: Build error when using @reference '#globals'; in CSS modules.
Fix Option 1: Add imports to package.json:
{
"imports": {
"#globals": "./src/styles/globals.css"
}
}Fix Option 2: Change @reference to direct relative path:
@reference './styles/globals.css';Fix Option 3: Use design-foundation directly:
@reference '@accelint/design-foundation/styles';---
Issue: Multiple @apply directives break IDE plugins
Cause: Using separate @apply statements instead of single combined statement.
Symptom: Tailwind IDE extension doesn't provide autocomplete, sorting, or error detection.
Fix: Combine all classes into single @apply:
/* ✅ Correct - single @apply */
.button {
@apply bg-surface-default outline-1 outline-interactive p-m;
}
/* ❌ Wrong - multiple @apply statements */
.button {
@apply bg-surface-default;
@apply outline-1 outline-interactive;
@apply p-m;
}---
Issue: Tailwind defaults not working
Cause: Design foundation removes Tailwind's default theme values.
Symptom: Classes like text-gray-500, shadow-lg, text-base don't work or produce unexpected results.
Fix: Use only the semantic tokens and utilities provided by the design system. Don't rely on Tailwind defaults.
/* ✅ Correct - design foundation tokens */
.card {
@apply bg-surface-default fg-primary-bold shadow-elevation-raised-muted;
}
/* ❌ Wrong - Tailwind defaults (removed) */
.card {
@apply bg-gray-100 text-gray-900 shadow-lg;
}---
Issue: p-4 produces 4px instead of 16px
Cause: Design foundation uses 1:1 numeric spacing relationship, not Tailwind's 4px multiplier.
Symptom: p-4 produces 4px padding instead of expected 16px.
Fix: Use semantic spacing scale (xxs, xs, s, m, l, xl, xxl, oversized) as primary approach:
/* ✅ Correct - semantic scale */
.button {
@apply px-m py-xs; /* Semantic, adapts to context */
}
/* ⚠️ Acceptable but rare - numeric with 1:1 relationship */
.custom {
@apply p-16; /* 16px exactly, not 64px */
}---
Issue: Dark mode styles not applying
Cause: Using Tailwind's dark: variant prefix instead of semantic tokens.
Symptom: Styles don't adapt when theme changes from light to dark.
Fix: Use semantic tokens that automatically adapt to theme:
/* ✅ Correct - semantic tokens adapt automatically */
.card {
@apply bg-surface-default fg-primary-bold;
}
/* ❌ Wrong - manual theme handling */
.card {
@apply bg-white fg-black dark:bg-gray-900 dark:fg-white;
}---
Issue: Border affecting layout dimensions
Cause: Using border instead of outline classes.
Symptom: Element dimensions change when border is added, breaking layout alignment.
Fix: Use outline classes which overlay without affecting dimensions:
/* ✅ Correct - outline doesn't affect size */
.card {
@apply outline-1 outline-interactive;
}
/* ❌ Wrong - border adds to dimensions */
.card {
@apply border-2 border-gray-300;
}---
Diagnostic Checklist
When encountering issues, verify this setup checklist:
- [ ]
postcss.config.mjsincludes@accelint/postcss-tailwind-css-modulesplugin - [ ] Every CSS module has
@referencedirective at top - [ ] CSS entrypoint (
globals.cssor@accelint/design-foundation/styles) is imported FIRST in root layout - [ ] If using
#globals, package.json hasimportsfield with correct path - [ ] Using semantic tokens (
bg-surface-default) not Tailwind defaults (bg-gray-100) - [ ] Using semantic spacing (
p-m) not assuming 4px multiplier (p-4= 4px not 16px) - [ ] Single
@applystatement per rule (not multiple) - [ ] Using
@variantblocks not attribute selectors ([data-size="small"])
Variant System
Custom variant system using data attributes for component state and styling in @accelint/design-foundation.
How Variants Work
Variants use @custom-variant CSS directive to create selectors from data attributes:
/* Definition */
@custom-variant color-info (&:where([data-color="info"]));
/* Compiled to */
.color-info\:bg-surface-default:where([data-color="info"]) {
background: var(--bg-surface-default);
}
/* Applied in HTML */
<div data-color="info" class="color-info:bg-surface-default">
Info content
</div>Benefit: Type-safe, reusable states without arbitrary Tailwind variants.
Available Variants
Color Variants
Apply color context to components:
data-color="info" Info/informational context (blue)
data-color="success" Success context (green)
data-color="warning" Warning context (yellow)
data-color="danger" Danger/error context (red)
data-color="neutral" Neutral context (gray)
data-color="primary" Primary brand color
data-color="secondary" Secondary brand colorUsage:
<Badge data-color="success">Active</Badge>
<Alert data-color="warning">Warning message</Alert>
<Button data-color="danger">Delete</Button>Size Variants
Control component sizing:
data-size="small" Small variant (compact UI)
data-size="medium" Medium variant (default)
data-size="large" Large variant (prominent)Usage:
<Button data-size="small">Compact</Button>
<Input data-size="large" />
<Icon data-size="medium" />State Variants
Component interaction states:
data-state="active" Active/selected state
data-state="inactive" Inactive/unselected state
data-state="disabled" Disabled state
data-state="loading" Loading state
data-state="error" Error stateUsage:
<Tab data-state="active">Current Tab</Tab>
<Button data-state="loading">Saving...</Button>
<Input data-state="error" />Layout Variants
Component layout modes:
data-layout="horizontal" Horizontal arrangement
data-layout="vertical" Vertical arrangement
data-layout="grid" Grid arrangement
data-layout="stack" Stacked arrangementUsage:
<ButtonGroup data-layout="horizontal">
<Button>First</Button>
<Button>Second</Button>
</ButtonGroup>Orientation Variants
Directional variants:
data-orientation="horizontal" Horizontal orientation
data-orientation="vertical" Vertical orientationUsage:
<Slider data-orientation="vertical" />
<Divider data-orientation="horizontal" />Position Variants
Placement variants:
data-position="top" Top position
data-position="right" Right position
data-position="bottom" Bottom position
data-position="left" Left position
data-position="center" Center positionUsage:
<Tooltip data-position="top">Hover text</Tooltip>
<Popover data-position="right" />Styling with Variants
Basic Pattern
// Component HTML
<button data-color="primary" data-size="large">
Action
</button>
// CSS with variants
@layer components.l1 {
.custom-button {
@apply px-m py-s;
}
.custom-button[data-size="large"] {
@apply px-l py-m;
}
.custom-button[data-color="primary"] {
@apply bg-interactive-bold fg-inverse-bold;
}
}Combining Variants
Multiple variants compose naturally:
<Card
data-color="info"
data-size="large"
data-state="active"
>
Content
</Card>CSS matches all applicable variants:
[data-color="info"] { /* info styles */ }
[data-size="large"] { /* large styles */ }
[data-state="active"] { /* active styles */ }Variant with Utility Classes
// ✅ Correct - variants + utility classes
<Button
data-color="primary"
data-size="large"
className="shadow-elevation-raised-muted"
>
Submit
</Button>
// ❌ Wrong - arbitrary Tailwind variants instead
<Button className="data-[color=primary]:bg-blue-500 hover:[&>svg]:opacity-50">
Submit
</Button>React Aria Integration
Design Toolkit components use React Aria, which provides built-in data attributes:
// React Aria automatically adds data attributes
<Button>
{/* Results in data-hovered, data-pressed, data-focused when interactive */}
Click me
</Button>
// Style these states
@layer components.l1 {
.custom-button[data-hovered] {
@apply bg-surface-hover;
}
.custom-button[data-pressed] {
@apply bg-surface-raised;
}
}Common React Aria data attributes:
data-hovered- Hover statedata-pressed- Press/click statedata-focused- Focus statedata-focus-visible- Keyboard focus (not mouse)data-disabled- Disabled statedata-selected- Selected state (checkboxes, tabs)data-open- Open state (modals, dropdowns)
Creating Custom Variants
When built-in variants don't cover your needs:
/* Define custom variant in CSS */
@custom-variant severity-critical (&:where([data-severity="critical"]));
/* Use in components */
@layer components.l1 {
.alert[data-severity="critical"] {
@apply bg-critical-muted fg-inverse-bold outline-critical-bold;
}
}// Apply in component
<Alert data-severity="critical">
Critical alert message
</Alert>Anti-Patterns
❌ Don't Use Arbitrary Variants
// ❌ Wrong - arbitrary Tailwind variant
<div className="data-[color=info]:bg-blue-500">
Content
</div>
// ✅ Correct - defined variant
<div data-color="info" className="color-info:bg-surface-default">
Content
</div>❌ Don't Use Inline Styles for States
// ❌ Wrong - inline styles
<Button style={{ backgroundColor: isActive ? 'blue' : 'gray' }}>
Action
</Button>
// ✅ Correct - variant attribute
<Button data-state={isActive ? 'active' : 'inactive'}>
Action
</Button>❌ Don't Create Variants for One-Off Styles
// ❌ Wrong - variant for unique case
<div data-special-snowflake="true">Content</div>
// ✅ Correct - use className for one-offs
<div className="bg-special-case">Content</div>Conditional Variant Application
Use clsx for dynamic variants:
import { clsx } from 'clsx';
<Button
data-color={isDestructive ? 'danger' : 'primary'}
data-size={isCompact ? 'small' : 'medium'}
data-state={isLoading ? 'loading' : 'active'}
className={clsx(
'custom-class',
isHighlighted && 'outline-2'
)}
>
Action
</Button>Related skills
FAQ
Why use outline instead of border?
The skill requires outline because borders add to element dimensions while outlines overlay without affecting size.
Where should component styles live?
In CSS modules with a single grouped @apply per rule, not as inline Tailwind for full component styling.
What import should clsx use?
Import clsx from @accelint/design-foundation/lib/utils, not directly from the clsx package.