@gallop.software/canon 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,78 @@
1
+ # Pattern 003: Typography Components
2
+
3
+ **Canon Version:** 1.0
4
+ **Status:** Stable
5
+ **Category:** Typography
6
+ **Enforcement:** ESLint
7
+
8
+ ## Decision
9
+
10
+ Use `Paragraph` and `Span` components instead of raw `<p>` and `<span>` elements in blocks and components.
11
+
12
+ ## Rationale
13
+
14
+ 1. **Consistent styling** — Typography components apply default margins, colors, and fonts
15
+ 2. **Prop-based customization** — Override styles via props, not className hunting
16
+ 3. **Design system compliance** — All text follows the same patterns
17
+ 4. **Easier refactoring** — Change defaults in one place
18
+
19
+ ## Examples
20
+
21
+ ### Bad
22
+
23
+ ```tsx
24
+ <p className="text-sm text-gray-500 mb-4">Some descriptive text</p>
25
+ <span className="text-green-500 font-medium">+28%</span>
26
+ ```
27
+
28
+ ### Good
29
+
30
+ ```tsx
31
+ <Paragraph fontSize="text-sm" color="text-gray-500">
32
+ Some descriptive text
33
+ </Paragraph>
34
+ <Span color="text-green-500" fontWeight="font-medium">+28%</Span>
35
+ ```
36
+
37
+ ## Available Typography Components
38
+
39
+ | Component | Default Tag | Default Margin | Use Case |
40
+ |-----------|-------------|----------------|----------|
41
+ | `Heading` | `h2` | `mb-8` | All headings |
42
+ | `Paragraph` | `p` | `mb-8` | Body text blocks |
43
+ | `Span` | `span` | `mb-0` | Inline text |
44
+ | `Label` | `p` | `mb-0` | Labels, captions |
45
+ | `Quote` | `blockquote` | `mb-8` | Quotations |
46
+ | `Accent` | `span` | `mb-4` | Decorative accent text |
47
+
48
+ ## Exceptions
49
+
50
+ Raw `<span>` is allowed when:
51
+
52
+ 1. **Inside typography components** — For inline styling within Heading, Paragraph, etc.
53
+ 2. **Gradient text effects** — When using `bg-clip-text` for gradient text
54
+
55
+ ```tsx
56
+ {/* Allowed: span inside Heading for gradient effect */}
57
+ <Heading>
58
+ Transform your space into something{' '}
59
+ <span className="bg-gradient-to-r from-purple-600 to-pink-500 bg-clip-text text-transparent">
60
+ extraordinary
61
+ </span>
62
+ </Heading>
63
+ ```
64
+
65
+ ## Enforcement
66
+
67
+ - **ESLint rule:** `gallop/prefer-typography-components`
68
+ - **Severity:** Warning
69
+
70
+ ## Escape Hatch
71
+
72
+ For gradient text or complex inline styling, raw `<span>` with `bg-clip-text` is allowed.
73
+
74
+ ## References
75
+
76
+ - `src/components/paragraph.tsx` — Paragraph component
77
+ - `src/components/span.tsx` — Span component
78
+ - `src/components/heading.tsx` — Heading component
@@ -0,0 +1,102 @@
1
+ # Pattern 004: Component Props Over className
2
+
3
+ **Canon Version:** 1.0
4
+ **Status:** Stable
5
+ **Category:** Typography
6
+ **Enforcement:** ESLint
7
+
8
+ ## Decision
9
+
10
+ Use dedicated props instead of `className` for styling that components support.
11
+
12
+ ## Rationale
13
+
14
+ 1. **Discoverable API** — Props are documented and autocompleted
15
+ 2. **Consistent defaults** — Components define sensible defaults
16
+ 3. **Easier auditing** — Can search for prop usage, not className patterns
17
+ 4. **Type safety** — Props can be typed, className cannot
18
+
19
+ ## Examples
20
+
21
+ ### Bad
22
+
23
+ ```tsx
24
+ <Heading className="text-center mb-6 text-accent">Title</Heading>
25
+ <Paragraph className="text-lg mb-4">Text content</Paragraph>
26
+ <Label className="text-sm font-semibold">Category</Label>
27
+ ```
28
+
29
+ ### Good
30
+
31
+ ```tsx
32
+ <Heading textAlign="text-center" margin="mb-6" color="text-accent">
33
+ Title
34
+ </Heading>
35
+ <Paragraph fontSize="text-lg" margin="mb-4">
36
+ Text content
37
+ </Paragraph>
38
+ <Label fontSize="text-sm" fontWeight="font-semibold">
39
+ Category
40
+ </Label>
41
+ ```
42
+
43
+ ## Supported Props by Component
44
+
45
+ ### Heading
46
+ - `margin` — Bottom margin (e.g., `mb-8`, `mb-4`)
47
+ - `color` — Text color (e.g., `text-contrast`, `text-accent`)
48
+ - `textAlign` — Alignment (e.g., `text-center`, `text-left`)
49
+ - `fontSize` — Size (e.g., `text-4xl`, `text-2xl`)
50
+ - `fontWeight` — Weight (e.g., `font-bold`, `font-semibold`)
51
+
52
+ ### Paragraph
53
+ - `margin` — Bottom margin
54
+ - `color` — Text color
55
+ - `textAlign` — Alignment
56
+ - `fontSize` — Size
57
+ - `lineHeight` — Line height
58
+
59
+ ### Label
60
+ - `margin` — Bottom margin
61
+ - `color` — Text color
62
+ - `textAlign` — Alignment
63
+ - `fontSize` — Size
64
+ - `fontWeight` — Weight
65
+
66
+ ### Accent
67
+ - `margin` — Bottom margin
68
+ - `color` — Text color
69
+ - `textAlign` — Alignment
70
+
71
+ ### Button
72
+ - `margin` — Bottom margin
73
+
74
+ ## When to Use className
75
+
76
+ Use `className` for styles that are NOT covered by props:
77
+
78
+ ```tsx
79
+ {/* max-w is not a prop, so className is appropriate */}
80
+ <Paragraph className="max-w-lg">
81
+ Content with constrained width
82
+ </Paragraph>
83
+ ```
84
+
85
+ ## Enforcement
86
+
87
+ - **ESLint rule:** `gallop/prefer-component-props`
88
+ - **Severity:** Warning
89
+ - **Detected patterns:** `mb-*`, `my-*`, `m-*`, `text-center`, `text-left`, `text-right`, `font-*`, `text-{color}` in className
90
+
91
+ ## Escape Hatch
92
+
93
+ If a component doesn't support a prop you need:
94
+
95
+ 1. Consider adding the prop to the component
96
+ 2. Use className as a fallback with a comment explaining why
97
+
98
+ ## References
99
+
100
+ - `src/components/heading.tsx` — Heading with prop overrides
101
+ - `src/components/paragraph.tsx` — Paragraph with prop overrides
102
+ - `src/components/label.tsx` — Label with prop overrides
@@ -0,0 +1,110 @@
1
+ # Pattern 005: Page Structure
2
+
3
+ **Canon Version:** 1.0
4
+ **Status:** Stable
5
+ **Category:** Structure
6
+ **Enforcement:** Documentation
7
+
8
+ ## Decision
9
+
10
+ Page files follow a consistent structure using `PageWrapper` and `generatePageMetadata`.
11
+
12
+ ## Rationale
13
+
14
+ 1. **Consistent metadata** — All pages have proper SEO metadata
15
+ 2. **Structured data** — Schema.org markup applied uniformly
16
+ 3. **Predictable layout** — Pages follow the same wrapper pattern
17
+ 4. **Easy auditing** — Can verify metadata completeness across all pages
18
+
19
+ ## Template
20
+
21
+ ```tsx
22
+ import { PageWrapper } from '@/components/page-wrapper'
23
+ import { generatePageMetadata, type PageMetadata } from '@/utils/page-helpers'
24
+
25
+ import Hero1 from '@/blocks/hero-1'
26
+ import Section1 from '@/blocks/section-1'
27
+
28
+ function Content() {
29
+ return (
30
+ <>
31
+ <Hero1 />
32
+ <Section1 />
33
+ </>
34
+ )
35
+ }
36
+
37
+ const metadata: PageMetadata = {
38
+ title: 'Page Title | Site Name',
39
+ description: 'Page description for SEO (150-160 characters)',
40
+ keywords: ['keyword1', 'keyword2', 'keyword3'],
41
+ focusKeyword: 'primary keyword',
42
+ readingTimeMinutes: 5,
43
+ publishDate: '2026-01-15T00:00:00Z',
44
+ modifiedDate: '2026-01-15T00:00:00Z',
45
+ featuredImage: '/images/page-featured.jpg',
46
+ alternates: {
47
+ canonical: 'https://example.com/page-slug',
48
+ },
49
+ authors: [{ name: 'Author Name' }],
50
+ openGraph: {
51
+ type: 'website',
52
+ locale: 'en_US',
53
+ url: 'https://example.com/page-slug',
54
+ siteName: 'Site Name',
55
+ title: 'Page Title | Site Name',
56
+ description: 'Page description for SEO',
57
+ image: {
58
+ url: '/images/page-featured.jpg',
59
+ alt: 'Featured image description',
60
+ },
61
+ },
62
+ twitter: {
63
+ card: 'summary_large_image',
64
+ site: '@sitehandle',
65
+ creator: '@creatorhandle',
66
+ title: 'Page Title',
67
+ description: 'Page description for Twitter',
68
+ image: '/images/page-featured.jpg',
69
+ },
70
+ structuredData: [
71
+ {
72
+ '@context': 'https://schema.org',
73
+ '@type': 'WebPage',
74
+ name: 'Page Title',
75
+ description: 'Page description',
76
+ },
77
+ ],
78
+ }
79
+
80
+ export const generateMetadata = () => generatePageMetadata(metadata)
81
+ export default function Page() {
82
+ return (
83
+ <PageWrapper metadata={metadata}>
84
+ <Content />
85
+ </PageWrapper>
86
+ )
87
+ }
88
+ ```
89
+
90
+ ## Required Metadata Properties
91
+
92
+ | Property | Required | Description |
93
+ |----------|----------|-------------|
94
+ | `title` | Yes | Page title with site name |
95
+ | `description` | Yes | SEO description (150-160 chars) |
96
+ | `keywords` | Yes | Array of relevant keywords |
97
+ | `focusKeyword` | Yes | Primary SEO keyword |
98
+ | `featuredImage` | Yes | OG image path |
99
+ | `alternates.canonical` | Yes | Canonical URL |
100
+
101
+ ## Enforcement
102
+
103
+ - **Method:** Code review / Documentation
104
+ - **Future:** CI validation of metadata completeness
105
+
106
+ ## References
107
+
108
+ - `src/app/(hero)/page.tsx` — Homepage example
109
+ - `src/components/page-wrapper.tsx` — PageWrapper component
110
+ - `src/utils/page-helpers.ts` — Metadata utilities
@@ -0,0 +1,87 @@
1
+ # Pattern 006: Block Naming
2
+
3
+ **Canon Version:** 1.0
4
+ **Status:** Stable
5
+ **Category:** Structure
6
+ **Enforcement:** Documentation
7
+
8
+ ## Decision
9
+
10
+ Block files use `{type}-{n}.tsx` naming with PascalCase function exports.
11
+
12
+ ## Rationale
13
+
14
+ 1. **Sequential organization** — Numbers indicate variants, not priority
15
+ 2. **Easy discovery** — Group by type in file explorer
16
+ 3. **No conflicts** — Numbers prevent naming collisions
17
+ 4. **Consistent exports** — PascalCase matches React conventions
18
+
19
+ ## Naming Rules
20
+
21
+ ### File Names
22
+
23
+ - Lowercase with hyphens: `hero-1.tsx`, `section-15.tsx`
24
+ - Type prefix followed by number: `{type}-{n}.tsx`
25
+ - Check existing files to find next available number
26
+
27
+ ### Function Exports
28
+
29
+ - PascalCase matching file name
30
+ - `hero-16.tsx` → `function Hero16()`
31
+ - `call-to-action-3.tsx` → `function CallToAction3()`
32
+
33
+ ## Block Types
34
+
35
+ | Type | Description |
36
+ |------|-------------|
37
+ | `hero-{n}` | Hero/banner sections |
38
+ | `section-{n}` | General content sections |
39
+ | `content-{n}` | Content/feature sections |
40
+ | `showcase-{n}` | Portfolio/gallery showcases |
41
+ | `cover-{n}` | Full-width image/video covers |
42
+ | `contact-{n}` | Contact forms and info |
43
+ | `testimonial-{n}` | Testimonials |
44
+ | `blog-{n}` | Blog-related sections |
45
+ | `pricing-{n}` | Pricing tables |
46
+ | `process-{n}` | Process/steps sections |
47
+ | `about-{n}` | About sections |
48
+ | `services-{n}` | Services sections |
49
+ | `call-to-action-{n}` | CTA sections |
50
+ | `partners-{n}` | Partner/logo clouds |
51
+ | `accordion-{n}` | FAQ/accordion sections |
52
+ | `archive-{n}` | Archive/listing sections |
53
+ | `application-{n}` | Job application forms |
54
+ | `business-info-{n}` | Business information |
55
+ | `sidebar-{n}` | Sidebar panels |
56
+ | `portfolio-{n}` | Portfolio grids |
57
+
58
+ ## Examples
59
+
60
+ ### Good
61
+
62
+ ```
63
+ src/blocks/
64
+ ├── hero-1.tsx → export default function Hero1()
65
+ ├── hero-2.tsx → export default function Hero2()
66
+ ├── section-1.tsx → export default function Section1()
67
+ ├── call-to-action-1.tsx → export default function CallToAction1()
68
+ ```
69
+
70
+ ### Bad
71
+
72
+ ```
73
+ src/blocks/
74
+ ├── Hero.tsx ❌ No number
75
+ ├── hero_1.tsx ❌ Underscore instead of hyphen
76
+ ├── HeroSection.tsx ❌ Wrong naming pattern
77
+ ├── cta-1.tsx ❌ Abbreviation not in type list
78
+ ```
79
+
80
+ ## Enforcement
81
+
82
+ - **Method:** Code review / Documentation
83
+ - **Future:** CI validation of naming patterns
84
+
85
+ ## References
86
+
87
+ - `src/blocks/` — All block files follow this pattern
@@ -0,0 +1,113 @@
1
+ # Pattern 007: Import Paths
2
+
3
+ **Canon Version:** 1.0
4
+ **Status:** Stable
5
+ **Category:** Structure
6
+ **Enforcement:** Documentation
7
+
8
+ ## Decision
9
+
10
+ Use `@/` path aliases for all internal imports. Use destructured imports for components.
11
+
12
+ ## Rationale
13
+
14
+ 1. **Consistent paths** — No relative path gymnastics (`../../..`)
15
+ 2. **Refactoring safe** — Moving files doesn't break imports
16
+ 3. **Clear origin** — `@/` indicates project source
17
+ 4. **Smaller bundles** — Destructured imports enable tree shaking
18
+
19
+ ## Path Aliases
20
+
21
+ | Alias | Resolves To | Example |
22
+ |-------|-------------|---------|
23
+ | `@/components` | `src/components` | `import { Heading } from '@/components'` |
24
+ | `@/blocks` | `src/blocks` | `import Hero1 from '@/blocks/hero-1'` |
25
+ | `@/hooks` | `src/hooks` | `import { useInView } from '@/hooks/use-in-view'` |
26
+ | `@/template` | `src/template` | `import PageFooter from '@/template/page-footer'` |
27
+ | `@/utils` | `src/utils` | `import { cn } from '@/utils/cn'` |
28
+
29
+ ## Import Patterns
30
+
31
+ ### Components (Destructured)
32
+
33
+ ```tsx
34
+ // Good: Destructured import from barrel
35
+ import { Heading, Paragraph, Button, Section, Columns, Column } from '@/components'
36
+
37
+ // Bad: Individual file imports
38
+ import { Heading } from '@/components/heading'
39
+ import { Paragraph } from '@/components/paragraph'
40
+ ```
41
+
42
+ ### Blocks (Default Export)
43
+
44
+ ```tsx
45
+ // Good: Default import with descriptive name
46
+ import Hero1 from '@/blocks/hero-1'
47
+ import Section3 from '@/blocks/section-3'
48
+
49
+ // Bad: Named import (blocks use default exports)
50
+ import { Hero1 } from '@/blocks/hero-1'
51
+ ```
52
+
53
+ ### Hooks (Named or Default)
54
+
55
+ ```tsx
56
+ // Init components (return null, used for side effects)
57
+ import CircleAnimationInit from '@/hooks/use-circle-animation'
58
+ import SwiperSliderInit from '@/hooks/swiper-slider-init'
59
+
60
+ // Traditional hooks
61
+ import { useInView } from '@/hooks/use-in-view'
62
+ ```
63
+
64
+ ### Icons
65
+
66
+ ```tsx
67
+ // Good: Import from Iconify packages
68
+ import arrowRightIcon from '@iconify/icons-heroicons/arrow-right-20-solid'
69
+ import playCircleIcon from '@iconify/icons-lucide/play-circle'
70
+
71
+ // Use with Icon component
72
+ <Icon icon={arrowRightIcon} className="w-5 h-5" />
73
+ ```
74
+
75
+ ## Examples
76
+
77
+ ### Good
78
+
79
+ ```tsx
80
+ import {
81
+ Heading,
82
+ Paragraph,
83
+ Button,
84
+ Section,
85
+ Columns,
86
+ Column,
87
+ Image,
88
+ } from '@/components'
89
+ import Hero1 from '@/blocks/hero-1'
90
+ import arrowDownIcon from '@iconify/icons-heroicons/arrow-down-20-solid'
91
+ ```
92
+
93
+ ### Bad
94
+
95
+ ```tsx
96
+ // Relative imports
97
+ import { Heading } from '../../components/heading'
98
+ import Hero1 from '../blocks/hero-1'
99
+
100
+ // Wrong alias format
101
+ import { Heading } from 'components/heading'
102
+ import { Heading } from '~/components/heading'
103
+ ```
104
+
105
+ ## Enforcement
106
+
107
+ - **Method:** Code review / ESLint import rules
108
+ - **Future:** Custom ESLint rule for path validation
109
+
110
+ ## References
111
+
112
+ - `tsconfig.json` — Path alias configuration
113
+ - `src/components/index.ts` — Component barrel file
@@ -0,0 +1,97 @@
1
+ # Pattern 008: Tailwind Only
2
+
3
+ **Canon Version:** 1.0
4
+ **Status:** Stable
5
+ **Category:** Styling
6
+ **Enforcement:** Documentation
7
+
8
+ ## Decision
9
+
10
+ Use Tailwind CSS classes exclusively. Do not use inline styles or CSS-in-JS.
11
+
12
+ ## Rationale
13
+
14
+ 1. **Consistent styling** — All styles come from the same system
15
+ 2. **Design tokens** — Tailwind enforces spacing/color scales
16
+ 3. **Performance** — No runtime CSS generation
17
+ 4. **Scannable code** — Styles are visible in the JSX
18
+ 5. **Tooling support** — Tailwind IntelliSense, Prettier sorting
19
+
20
+ ## Examples
21
+
22
+ ### Good
23
+
24
+ ```tsx
25
+ <div className="flex items-center gap-4 p-6 bg-white rounded-lg shadow-lg">
26
+ <Heading className="text-2xl">Title</Heading>
27
+ </div>
28
+ ```
29
+
30
+ ### Bad
31
+
32
+ ```tsx
33
+ // Inline styles
34
+ <div style={{ display: 'flex', padding: '24px', backgroundColor: 'white' }}>
35
+ <Heading>Title</Heading>
36
+ </div>
37
+
38
+ // CSS-in-JS
39
+ const StyledDiv = styled.div`
40
+ display: flex;
41
+ padding: 24px;
42
+ `;
43
+ ```
44
+
45
+ ## Allowed Exceptions
46
+
47
+ ### Dynamic Values
48
+
49
+ When a style value comes from data or calculations:
50
+
51
+ ```tsx
52
+ // Allowed: Dynamic positioning
53
+ <div style={{ left: `${position}px` }}>
54
+
55
+ // Allowed: CSS custom properties for theming
56
+ <div style={{ '--progress': `${percent}%` } as React.CSSProperties}>
57
+ ```
58
+
59
+ ### CSS Custom Properties
60
+
61
+ ```tsx
62
+ // Allowed: Using CSS variables
63
+ <div className="bg-[var(--color-accent)]">
64
+ ```
65
+
66
+ ### Complex Animations
67
+
68
+ ```tsx
69
+ // Allowed: Framer Motion
70
+ <motion.div animate={{ opacity: 1 }}>
71
+ ```
72
+
73
+ ## Conditional Classes
74
+
75
+ Use `clsx` for conditional classes:
76
+
77
+ ```tsx
78
+ import { clsx } from 'clsx'
79
+
80
+ <button
81
+ className={clsx(
82
+ 'px-4 py-2 rounded',
83
+ isActive && 'bg-accent text-white',
84
+ !isActive && 'bg-gray-100 text-gray-700'
85
+ )}
86
+ >
87
+ ```
88
+
89
+ ## Enforcement
90
+
91
+ - **Method:** Code review / Documentation
92
+ - **Future:** ESLint rule to detect inline styles
93
+
94
+ ## References
95
+
96
+ - `src/styles/tailwind.css` — Tailwind configuration
97
+ - All blocks and components use Tailwind exclusively
@@ -0,0 +1,114 @@
1
+ # Pattern 009: Color Tokens
2
+
3
+ **Canon Version:** 1.0
4
+ **Status:** Stable
5
+ **Category:** Styling
6
+ **Enforcement:** Documentation
7
+
8
+ ## Decision
9
+
10
+ Use semantic color tokens instead of raw color values. Colors are defined as CSS custom properties.
11
+
12
+ ## Rationale
13
+
14
+ 1. **Theme consistency** — Colors change in one place
15
+ 2. **Semantic meaning** — `text-contrast` is clearer than `text-gray-800`
16
+ 3. **Dark mode ready** — Token values can change per theme
17
+ 4. **Accessibility** — Contrast ratios are pre-validated
18
+
19
+ ## Color Token System
20
+
21
+ ### Text Colors
22
+
23
+ | Token | Usage |
24
+ |-------|-------|
25
+ | `text-body` | Default body text |
26
+ | `text-contrast` | High-contrast text (headings, emphasis) |
27
+ | `text-accent` | Accent/brand color text |
28
+ | `text-accent-contrast` | Text on accent backgrounds |
29
+ | `text-white` | White text |
30
+
31
+ ### Background Colors
32
+
33
+ | Token | Usage |
34
+ |-------|-------|
35
+ | `bg-body` | Main page background |
36
+ | `bg-body2` | Secondary/alternate background |
37
+ | `bg-contrast` | High-contrast background |
38
+ | `bg-accent` | Primary accent background |
39
+ | `bg-accent2` | Secondary accent background |
40
+ | `bg-accent3` | Tertiary accent background |
41
+
42
+ ### Pairing Rules
43
+
44
+ Accent backgrounds pair with contrast variants:
45
+
46
+ ```tsx
47
+ // Good: Proper pairing
48
+ <div className="bg-accent text-accent-contrast">
49
+ Readable text on accent background
50
+ </div>
51
+
52
+ // Bad: May have contrast issues
53
+ <div className="bg-accent text-body">
54
+ Potentially unreadable
55
+ </div>
56
+ ```
57
+
58
+ ## Examples
59
+
60
+ ### Good
61
+
62
+ ```tsx
63
+ <Section className="bg-body2">
64
+ <Heading color="text-contrast">Title</Heading>
65
+ <Paragraph color="text-body">Body content</Paragraph>
66
+ <Button className="bg-accent text-accent-contrast">
67
+ Call to Action
68
+ </Button>
69
+ </Section>
70
+ ```
71
+
72
+ ### Bad
73
+
74
+ ```tsx
75
+ <Section className="bg-gray-100">
76
+ <Heading className="text-gray-900">Title</Heading>
77
+ <Paragraph className="text-gray-600">Body content</Paragraph>
78
+ <Button className="bg-purple-600 text-white">
79
+ Call to Action
80
+ </Button>
81
+ </Section>
82
+ ```
83
+
84
+ ## CSS Variable Definitions
85
+
86
+ ```css
87
+ :root {
88
+ --color-body: #171412;
89
+ --color-body2: #f2eae7;
90
+ --color-contrast: #1a1a1a;
91
+ --color-accent: #8b5a4a;
92
+ --color-accent2: #6b4a3a;
93
+ --color-accent3: #f2ebe1;
94
+ --color-accent-contrast: #ffffff;
95
+ }
96
+ ```
97
+
98
+ ## When to Use Raw Colors
99
+
100
+ Raw Tailwind colors are acceptable for:
101
+
102
+ 1. **Third-party content** — Colors from external data
103
+ 2. **One-off decorative elements** — Gradients, shadows
104
+ 3. **Status indicators** — `text-green-500` for success
105
+
106
+ ## Enforcement
107
+
108
+ - **Method:** Code review / Documentation
109
+ - **Future:** ESLint rule to prefer tokens
110
+
111
+ ## References
112
+
113
+ - `src/styles/tailwind.css` — Color token definitions
114
+ - `src/components/button.tsx` — Button using accent tokens