@citron-systems/citron-ds 1.0.0 → 1.0.1
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.
- package/README.md +163 -149
- package/cli/citron-mascot.mjs +117 -0
- package/dist/ai/inkblot-ai-reference.json +582 -570
- package/dist/ai/inkblot-tokens-resolved.json +25 -0
- package/dist/ai/inkblot-tokens-schema.json +54 -54
- package/dist/brand/citron-mascot-mono.svg +17 -0
- package/dist/brand/citron-mascot.svg +21 -0
- package/dist/bundle/README.md +28 -0
- package/dist/bundle/ai-reference.json +582 -0
- package/dist/bundle/brand/citron-mascot-mono.svg +17 -0
- package/dist/bundle/brand/citron-mascot.svg +21 -0
- package/dist/bundle/preview/mascot.html +72 -0
- package/dist/bundle/system/ai.json +168 -0
- package/dist/bundle/system/cli.json +340 -0
- package/dist/bundle/system/components.json +304 -0
- package/dist/bundle/system/content.json +168 -0
- package/dist/bundle/system/devtools.json +95 -0
- package/dist/bundle/system/foundations.json +121 -0
- package/dist/bundle/system/grid.json +101 -0
- package/dist/bundle/system/icons.json +507 -0
- package/dist/bundle/system/index.json +57 -0
- package/dist/bundle/system/inkblot-ai-reference.json +582 -0
- package/dist/bundle/system/motion.json +159 -0
- package/dist/bundle/tokens/primitive/breakpoint.tokens.json +14 -0
- package/dist/bundle/tokens/primitive/color.tokens.json +71 -0
- package/dist/bundle/tokens/primitive/devtools.palette.tokens.json +55 -0
- package/dist/bundle/tokens/primitive/duration.tokens.json +38 -0
- package/dist/bundle/tokens/primitive/grid.tokens.json +34 -0
- package/dist/bundle/tokens/primitive/radius.tokens.json +26 -0
- package/dist/bundle/tokens/primitive/shadow.tokens.json +41 -0
- package/dist/bundle/tokens/primitive/spacing.tokens.json +38 -0
- package/dist/bundle/tokens/primitive/typography.tokens.json +60 -0
- package/dist/bundle/tokens/primitive/zindex.tokens.json +19 -0
- package/dist/bundle/tokens/semantic/dark.tokens.json +43 -0
- package/dist/bundle/tokens/semantic/devtools.semantic.tokens.json +79 -0
- package/dist/bundle/tokens/semantic/inkblot.semantic.tokens.json +140 -0
- package/dist/bundle/tokens-schema.json +54 -0
- package/dist/bundle/tokens.flat.json +224 -0
- package/dist/bundle/tokens.resolved.json +224 -0
- package/dist/bundle/variables.css +228 -0
- package/dist/bundle/variables.scss +225 -0
- package/dist/css/inkblot-variables.css +25 -0
- package/dist/index.html +956 -0
- package/dist/js/inkblot-tokens.js +25 -0
- package/dist/js/inkblot-tokens.json +25 -0
- package/dist/preview/mascot.html +72 -0
- package/dist/scss/_inkblot-variables.scss +25 -0
- package/package.json +73 -61
- package/tokens/primitive/breakpoint.tokens.json +14 -14
- package/tokens/primitive/color.tokens.json +71 -71
- package/tokens/primitive/devtools.palette.tokens.json +55 -0
- package/tokens/primitive/duration.tokens.json +38 -38
- package/tokens/primitive/grid.tokens.json +34 -34
- package/tokens/primitive/radius.tokens.json +26 -26
- package/tokens/primitive/shadow.tokens.json +41 -41
- package/tokens/primitive/spacing.tokens.json +38 -38
- package/tokens/primitive/typography.tokens.json +60 -60
- package/tokens/primitive/zindex.tokens.json +19 -19
- package/tokens/semantic/dark.tokens.json +43 -43
- package/tokens/semantic/devtools.semantic.tokens.json +79 -0
- package/tokens/semantic/inkblot.semantic.tokens.json +140 -140
package/dist/index.html
ADDED
|
@@ -0,0 +1,956 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Citron — Design System</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
|
10
|
+
<link rel="stylesheet" href="/css/inkblot-variables.css" />
|
|
11
|
+
<style>
|
|
12
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; }
|
|
13
|
+
html { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; scroll-behavior: smooth; }
|
|
14
|
+
|
|
15
|
+
body {
|
|
16
|
+
font-family: var(--inkblot-typography-font-family-sans);
|
|
17
|
+
background: var(--inkblot-semantic-color-background-primary);
|
|
18
|
+
color: var(--inkblot-semantic-color-text-primary);
|
|
19
|
+
line-height: var(--inkblot-typography-line-height-normal);
|
|
20
|
+
min-height: 100vh;
|
|
21
|
+
transition: background var(--inkblot-duration-slow) var(--inkblot-easing-default),
|
|
22
|
+
color var(--inkblot-duration-slow) var(--inkblot-easing-default);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
[data-theme="dark"] {
|
|
26
|
+
--inkblot-semantic-color-background-primary: var(--inkblot-dark-color-background-primary);
|
|
27
|
+
--inkblot-semantic-color-background-secondary: var(--inkblot-dark-color-background-secondary);
|
|
28
|
+
--inkblot-semantic-color-background-tertiary: var(--inkblot-dark-color-background-tertiary);
|
|
29
|
+
--inkblot-semantic-color-background-inverse: var(--inkblot-dark-color-background-inverse);
|
|
30
|
+
--inkblot-semantic-color-text-primary: var(--inkblot-dark-color-text-primary);
|
|
31
|
+
--inkblot-semantic-color-text-secondary: var(--inkblot-dark-color-text-secondary);
|
|
32
|
+
--inkblot-semantic-color-text-tertiary: var(--inkblot-dark-color-text-tertiary);
|
|
33
|
+
--inkblot-semantic-color-text-inverse: var(--inkblot-dark-color-text-inverse);
|
|
34
|
+
--inkblot-semantic-color-text-link: var(--inkblot-dark-color-text-link);
|
|
35
|
+
--inkblot-semantic-color-text-link-hover: var(--inkblot-dark-color-text-link-hover);
|
|
36
|
+
--inkblot-semantic-color-border-default: var(--inkblot-dark-color-border-default);
|
|
37
|
+
--inkblot-semantic-color-border-strong: var(--inkblot-dark-color-border-strong);
|
|
38
|
+
--inkblot-semantic-color-border-focus: var(--inkblot-dark-color-border-focus);
|
|
39
|
+
--inkblot-semantic-color-interactive-primary: var(--inkblot-dark-color-interactive-primary);
|
|
40
|
+
--inkblot-semantic-color-interactive-primary-hover: var(--inkblot-dark-color-interactive-primary-hover);
|
|
41
|
+
--inkblot-semantic-color-interactive-primary-active: var(--inkblot-dark-color-interactive-primary-active);
|
|
42
|
+
--inkblot-semantic-color-interactive-secondary: var(--inkblot-dark-color-interactive-secondary);
|
|
43
|
+
--inkblot-semantic-color-interactive-secondary-hover: var(--inkblot-dark-color-interactive-secondary-hover);
|
|
44
|
+
--inkblot-semantic-color-status-success: var(--inkblot-dark-color-status-success);
|
|
45
|
+
--inkblot-semantic-color-status-warning: var(--inkblot-dark-color-status-warning);
|
|
46
|
+
--inkblot-semantic-color-status-error: var(--inkblot-dark-color-status-error);
|
|
47
|
+
--inkblot-semantic-color-status-info: var(--inkblot-dark-color-status-info);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* NAV */
|
|
51
|
+
.nav { position: sticky; top: 0; z-index: 20; backdrop-filter: blur(24px) saturate(1.2); -webkit-backdrop-filter: blur(24px) saturate(1.2); background: rgba(250,250,247,0.78); border-bottom: 1px solid var(--inkblot-semantic-color-border-default); padding: var(--inkblot-spacing-3) var(--inkblot-spacing-6); display: flex; align-items: center; justify-content: space-between; }
|
|
52
|
+
[data-theme="dark"] .nav { background: rgba(18,17,14,0.78); }
|
|
53
|
+
.nav-brand { font-size: var(--inkblot-typography-font-size-sm); font-weight: 600; letter-spacing: -0.01em; color: var(--inkblot-semantic-color-text-primary); text-decoration: none; transition: opacity 150ms; }
|
|
54
|
+
.nav-brand:hover { opacity: 0.7; }
|
|
55
|
+
.nav-menu { display: contents; }
|
|
56
|
+
.nav-links { display: flex; gap: var(--inkblot-spacing-4); list-style: none; padding: 0; margin: 0; }
|
|
57
|
+
.nav-links a { font-size: var(--inkblot-typography-font-size-xs); color: var(--inkblot-semantic-color-text-secondary); text-decoration: none; white-space: nowrap; transition: color 150ms; padding: var(--inkblot-spacing-2) 0; }
|
|
58
|
+
.nav-links a:hover { color: var(--inkblot-semantic-color-text-primary); }
|
|
59
|
+
.nav-hamburger { display: none; width: 44px; height: 44px; padding: 0; border: none; background: transparent; cursor: pointer; align-items: center; justify-content: center; border-radius: var(--inkblot-radius-md); transition: background 150ms; position: relative; flex-shrink: 0; }
|
|
60
|
+
.nav-hamburger:hover { background: var(--inkblot-semantic-color-background-secondary); }
|
|
61
|
+
.nav-hamburger span, .nav-hamburger span::before, .nav-hamburger span::after { display: block; width: 20px; height: 2px; background: var(--inkblot-semantic-color-text-primary); border-radius: 1px; transition: transform 200ms, opacity 200ms; }
|
|
62
|
+
.nav-hamburger span { position: relative; }
|
|
63
|
+
.nav-hamburger span::before, .nav-hamburger span::after { content: ''; position: absolute; left: 0; top: 0; }
|
|
64
|
+
.nav-hamburger span::before { transform: translateY(-6px); }
|
|
65
|
+
.nav-hamburger span::after { transform: translateY(6px); }
|
|
66
|
+
.nav.open .nav-hamburger span { background: transparent; }
|
|
67
|
+
.nav.open .nav-hamburger span::before { transform: translateY(0) rotate(45deg); }
|
|
68
|
+
.nav.open .nav-hamburger span::after { transform: translateY(0) rotate(-45deg); }
|
|
69
|
+
.theme-toggle { width: 44px; height: 24px; background: var(--inkblot-semantic-color-border-default); border: none; border-radius: var(--inkblot-radius-full); cursor: pointer; position: relative; transition: background 150ms; flex-shrink: 0; }
|
|
70
|
+
.theme-toggle::after { content: ''; position: absolute; top: 2px; left: 2px; width: 20px; height: 20px; background: white; border-radius: 50%; box-shadow: 0 1px 3px rgba(29,28,25,0.12); transition: transform var(--inkblot-duration-fast) var(--inkblot-easing-expressive); }
|
|
71
|
+
[data-theme="dark"] .theme-toggle { background: var(--inkblot-dark-color-interactive-primary); }
|
|
72
|
+
[data-theme="dark"] .theme-toggle::after { transform: translateX(20px); }
|
|
73
|
+
*:focus-visible { outline: 2px solid var(--inkblot-semantic-color-border-focus); outline-offset: 2px; }
|
|
74
|
+
|
|
75
|
+
/* HERO */
|
|
76
|
+
.hero { padding: clamp(64px,10vh,120px) var(--inkblot-spacing-8); text-align: center; max-width: 720px; margin: 0 auto; }
|
|
77
|
+
.hero h1 { font-size: clamp(2rem,5vw,2.75rem); font-weight: 600; letter-spacing: -0.03em; line-height: 1.15; margin-bottom: var(--inkblot-spacing-4); }
|
|
78
|
+
.hero .tagline { font-size: var(--inkblot-typography-font-size-md); color: var(--inkblot-semantic-color-text-secondary); line-height: 1.65; margin-bottom: var(--inkblot-spacing-10); }
|
|
79
|
+
.hero-actions { display: flex; gap: var(--inkblot-spacing-3); justify-content: center; flex-wrap: wrap; }
|
|
80
|
+
|
|
81
|
+
/* LAYOUT */
|
|
82
|
+
.container { max-width: 1200px; margin: 0 auto; padding: 0 var(--inkblot-spacing-8) var(--inkblot-spacing-24); }
|
|
83
|
+
.section { margin-bottom: var(--inkblot-spacing-20); }
|
|
84
|
+
.section-header { margin-bottom: var(--inkblot-spacing-8); padding-bottom: var(--inkblot-spacing-4); border-bottom: 1px solid var(--inkblot-semantic-color-border-default); }
|
|
85
|
+
.section-title { font-size: var(--inkblot-typography-font-size-xl); font-weight: 500; letter-spacing: -0.02em; margin-bottom: 4px; }
|
|
86
|
+
.section-desc { font-size: var(--inkblot-typography-font-size-xs); color: var(--inkblot-semantic-color-text-tertiary); }
|
|
87
|
+
.card { background: var(--inkblot-semantic-color-background-secondary); border-radius: var(--inkblot-radius-xl); padding: var(--inkblot-spacing-6); box-shadow: var(--inkblot-shadow-sm); }
|
|
88
|
+
.card-title { font-size: var(--inkblot-typography-font-size-sm); font-weight: 500; margin-bottom: var(--inkblot-spacing-4); color: var(--inkblot-semantic-color-text-primary); }
|
|
89
|
+
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: var(--inkblot-spacing-6); }
|
|
90
|
+
.three-col { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--inkblot-spacing-6); }
|
|
91
|
+
.four-col { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--inkblot-spacing-6); }
|
|
92
|
+
@media (max-width: 900px) { .three-col, .four-col { grid-template-columns: 1fr 1fr; } }
|
|
93
|
+
@media (max-width: 640px) { .two-col, .three-col, .four-col { grid-template-columns: 1fr; } }
|
|
94
|
+
|
|
95
|
+
/* BUTTONS */
|
|
96
|
+
.btn { padding: var(--inkblot-spacing-3) var(--inkblot-spacing-6); border: none; border-radius: var(--inkblot-radius-lg); font-family: inherit; font-size: var(--inkblot-typography-font-size-sm); font-weight: 500; cursor: pointer; transition: background var(--inkblot-duration-fast) var(--inkblot-easing-default), box-shadow var(--inkblot-duration-fast) var(--inkblot-easing-default), transform 80ms var(--inkblot-easing-default); min-height: 44px; display: inline-flex; align-items: center; justify-content: center; gap: var(--inkblot-spacing-2); }
|
|
97
|
+
.btn:active { transform: scale(0.98); }
|
|
98
|
+
.btn-primary { background: var(--inkblot-semantic-color-interactive-primary); color: var(--inkblot-color-accent-citron-50); box-shadow: 0 1px 4px rgba(196,160,48,0.15); }
|
|
99
|
+
.btn-primary:hover { background: var(--inkblot-semantic-color-interactive-primary-hover); box-shadow: 0 2px 10px rgba(196,160,48,0.22); }
|
|
100
|
+
.btn-secondary { background: var(--inkblot-semantic-color-background-secondary); color: var(--inkblot-semantic-color-text-primary); border: 1px solid var(--inkblot-semantic-color-border-default); box-shadow: var(--inkblot-shadow-xs); }
|
|
101
|
+
.btn-secondary:hover { background: var(--inkblot-semantic-color-background-tertiary); border-color: var(--inkblot-semantic-color-border-strong); box-shadow: var(--inkblot-shadow-sm); }
|
|
102
|
+
.btn-ghost { background: transparent; color: var(--inkblot-semantic-color-text-link); }
|
|
103
|
+
.btn-ghost:hover { background: var(--inkblot-color-accent-citron-50); }
|
|
104
|
+
.btn-danger { background: var(--inkblot-semantic-color-status-error); color: white; }
|
|
105
|
+
.btn-sm { min-height: 32px; padding: var(--inkblot-spacing-1) var(--inkblot-spacing-3); font-size: var(--inkblot-typography-font-size-xs); }
|
|
106
|
+
.btn-lg { min-height: 48px; padding: var(--inkblot-spacing-3) var(--inkblot-spacing-8); font-size: var(--inkblot-typography-font-size-md); border-radius: var(--inkblot-radius-xl); }
|
|
107
|
+
|
|
108
|
+
/* BADGES */
|
|
109
|
+
.badges { display: flex; gap: var(--inkblot-spacing-3); flex-wrap: wrap; }
|
|
110
|
+
.badge { padding: var(--inkblot-spacing-1) var(--inkblot-spacing-3); border-radius: var(--inkblot-radius-full); font-size: var(--inkblot-typography-font-size-xs); font-weight: 500; color: white; }
|
|
111
|
+
.badge-success { background: var(--inkblot-semantic-color-status-success); }
|
|
112
|
+
.badge-warning { background: var(--inkblot-semantic-color-status-warning); color: var(--inkblot-color-accent-citron-50); }
|
|
113
|
+
.badge-error { background: var(--inkblot-semantic-color-status-error); }
|
|
114
|
+
.badge-info { background: var(--inkblot-semantic-color-status-info); }
|
|
115
|
+
|
|
116
|
+
/* SWATCHES */
|
|
117
|
+
.swatch-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); gap: var(--inkblot-spacing-2); }
|
|
118
|
+
.swatch { border-radius: var(--inkblot-radius-lg); overflow: hidden; cursor: pointer; border: 1px solid transparent; transition: border-color 150ms, box-shadow 150ms; }
|
|
119
|
+
.swatch:hover { border-color: var(--inkblot-semantic-color-border-default); box-shadow: var(--inkblot-shadow-sm); }
|
|
120
|
+
.swatch.copied { border-color: var(--inkblot-semantic-color-status-success); }
|
|
121
|
+
.swatch-color { height: 56px; }
|
|
122
|
+
.swatch-meta { padding: 6px; font-size: 10px; font-weight: 500; color: var(--inkblot-semantic-color-text-secondary); background: var(--inkblot-semantic-color-background-secondary); }
|
|
123
|
+
|
|
124
|
+
/* TYPOGRAPHY */
|
|
125
|
+
.type-row { padding: var(--inkblot-spacing-3) var(--inkblot-spacing-3); border-bottom: 1px solid var(--inkblot-semantic-color-border-default); border-radius: var(--inkblot-radius-sm); transition: background 150ms; }
|
|
126
|
+
.type-row:hover { background: var(--inkblot-semantic-color-background-secondary); }
|
|
127
|
+
.type-row:last-child { border-bottom: none; }
|
|
128
|
+
.type-row .meta { font-size: 10px; font-weight: 500; color: var(--inkblot-semantic-color-text-tertiary); letter-spacing: 0.04em; text-transform: uppercase; margin-bottom: var(--inkblot-spacing-1); }
|
|
129
|
+
|
|
130
|
+
/* INPUTS */
|
|
131
|
+
.input-group { display: flex; flex-direction: column; gap: 4px; }
|
|
132
|
+
.input-label { font-size: var(--inkblot-typography-font-size-xs); font-weight: 500; color: var(--inkblot-semantic-color-text-secondary); }
|
|
133
|
+
.input { padding: var(--inkblot-spacing-2) var(--inkblot-spacing-3); border: 1px solid var(--inkblot-semantic-color-border-default); border-radius: var(--inkblot-radius-md); font-family: inherit; font-size: var(--inkblot-typography-font-size-base); background: var(--inkblot-semantic-color-background-primary); color: var(--inkblot-semantic-color-text-primary); min-height: 40px; transition: border-color 150ms, box-shadow 150ms; outline: none; }
|
|
134
|
+
.input:hover { border-color: var(--inkblot-semantic-color-border-strong); }
|
|
135
|
+
.input:focus { border-color: var(--inkblot-semantic-color-border-focus); box-shadow: 0 0 0 2px rgba(196,160,48,0.15); }
|
|
136
|
+
.input-error { border-color: var(--inkblot-semantic-color-status-error) !important; }
|
|
137
|
+
.input-error:focus { box-shadow: 0 0 0 2px rgba(190,62,53,0.15); }
|
|
138
|
+
.input-help { font-size: 11px; color: var(--inkblot-semantic-color-text-tertiary); }
|
|
139
|
+
.input-error-msg { font-size: 11px; color: var(--inkblot-semantic-color-status-error); }
|
|
140
|
+
|
|
141
|
+
/* TOGGLE */
|
|
142
|
+
.toggle-track { width: 44px; height: 24px; background: var(--inkblot-semantic-color-border-default); border-radius: var(--inkblot-radius-full); position: relative; cursor: pointer; border: none; transition: background 150ms; }
|
|
143
|
+
.toggle-track.on { background: var(--inkblot-semantic-color-interactive-primary); }
|
|
144
|
+
.toggle-thumb { width: 20px; height: 20px; background: white; border-radius: 50%; position: absolute; top: 2px; left: 2px; box-shadow: 0 1px 3px rgba(29,28,25,0.12); transition: transform var(--inkblot-duration-fast) var(--inkblot-easing-expressive); }
|
|
145
|
+
.toggle-track.on .toggle-thumb { transform: translateX(20px); }
|
|
146
|
+
|
|
147
|
+
/* SHADOWS */
|
|
148
|
+
.shadow-row { display: flex; flex-wrap: wrap; gap: var(--inkblot-spacing-6); }
|
|
149
|
+
.shadow-box { width: 100px; height: 100px; background: var(--inkblot-semantic-color-background-primary); border-radius: var(--inkblot-radius-xl); display: flex; align-items: center; justify-content: center; font-size: 11px; color: var(--inkblot-semantic-color-text-tertiary); transition: transform 150ms; }
|
|
150
|
+
.shadow-box:hover { transform: translateY(-3px); }
|
|
151
|
+
|
|
152
|
+
/* SPACING */
|
|
153
|
+
.scale-row { display: flex; flex-wrap: wrap; gap: var(--inkblot-spacing-4); align-items: flex-end; }
|
|
154
|
+
.scale-box { background: var(--inkblot-semantic-color-interactive-primary); border-radius: var(--inkblot-radius-md); display: flex; align-items: center; justify-content: center; color: var(--inkblot-color-accent-citron-50); font-size: 10px; font-weight: 500; }
|
|
155
|
+
.radius-row { display: flex; flex-wrap: wrap; gap: var(--inkblot-spacing-4); }
|
|
156
|
+
.radius-box { width: 72px; height: 72px; background: linear-gradient(135deg, var(--inkblot-color-accent-citron-400), var(--inkblot-color-accent-citron-600)); display: flex; align-items: center; justify-content: center; color: var(--inkblot-color-accent-citron-50); font-size: 11px; font-weight: 500; }
|
|
157
|
+
|
|
158
|
+
/* SKELETON */
|
|
159
|
+
.skeleton { border-radius: var(--inkblot-radius-md); background: linear-gradient(90deg, var(--inkblot-semantic-color-background-secondary) 25%, var(--inkblot-semantic-color-background-tertiary) 50%, var(--inkblot-semantic-color-background-secondary) 75%); background-size: 200% 100%; animation: shimmer 1.5s ease-in-out infinite; }
|
|
160
|
+
@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
|
|
161
|
+
|
|
162
|
+
/* TOASTS */
|
|
163
|
+
.toast { display: flex; align-items: center; gap: var(--inkblot-spacing-3); padding: var(--inkblot-spacing-3) var(--inkblot-spacing-4); border-radius: var(--inkblot-radius-lg); font-size: var(--inkblot-typography-font-size-sm); box-shadow: var(--inkblot-shadow-lg); }
|
|
164
|
+
.toast-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
|
|
165
|
+
.toast-info { background: rgba(87,144,173,0.07); border: 1px solid rgba(87,144,173,0.18); }
|
|
166
|
+
.toast-info .toast-dot { background: var(--inkblot-semantic-color-status-info); }
|
|
167
|
+
.toast-success { background: rgba(53,140,70,0.07); border: 1px solid rgba(53,140,70,0.18); }
|
|
168
|
+
.toast-success .toast-dot { background: var(--inkblot-semantic-color-status-success); }
|
|
169
|
+
.toast-error { background: rgba(190,62,53,0.07); border: 1px solid rgba(190,62,53,0.18); }
|
|
170
|
+
.toast-error .toast-dot { background: var(--inkblot-semantic-color-status-error); }
|
|
171
|
+
.toast-warning { background: rgba(196,140,26,0.07); border: 1px solid rgba(196,140,26,0.18); }
|
|
172
|
+
.toast-warning .toast-dot { background: var(--inkblot-semantic-color-status-warning); }
|
|
173
|
+
|
|
174
|
+
/* ERROR / EMPTY */
|
|
175
|
+
.error-demo { background: rgba(190,62,53,0.06); border: 1px solid rgba(190,62,53,0.2); border-radius: var(--inkblot-radius-lg); padding: var(--inkblot-spacing-4); font-size: var(--inkblot-typography-font-size-sm); }
|
|
176
|
+
[data-theme="dark"] .error-demo { background: rgba(224,90,82,0.1); border-color: rgba(224,90,82,0.25); }
|
|
177
|
+
.empty-state { text-align: center; padding: var(--inkblot-spacing-10) var(--inkblot-spacing-4); }
|
|
178
|
+
.empty-state h3 { font-size: var(--inkblot-typography-font-size-lg); font-weight: 600; margin-bottom: var(--inkblot-spacing-2); }
|
|
179
|
+
.empty-state p { font-size: var(--inkblot-typography-font-size-sm); color: var(--inkblot-semantic-color-text-secondary); margin-bottom: var(--inkblot-spacing-4); }
|
|
180
|
+
|
|
181
|
+
/* MOTION DEMO */
|
|
182
|
+
.motion-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: var(--inkblot-spacing-4); }
|
|
183
|
+
.motion-item { text-align: center; }
|
|
184
|
+
.motion-box { width: 100%; aspect-ratio: 1; background: var(--inkblot-semantic-color-interactive-primary); border-radius: var(--inkblot-radius-lg); display: flex; align-items: center; justify-content: center; color: var(--inkblot-color-accent-citron-50); font-size: 11px; font-weight: 500; margin-bottom: var(--inkblot-spacing-2); cursor: pointer; position: relative; overflow: hidden; }
|
|
185
|
+
.motion-label { font-size: 10px; color: var(--inkblot-semantic-color-text-tertiary); font-weight: 500; }
|
|
186
|
+
.motion-box.hover-lift { transition: transform var(--inkblot-duration-fast) var(--inkblot-easing-default), box-shadow var(--inkblot-duration-fast) var(--inkblot-easing-default); }
|
|
187
|
+
.motion-box.hover-lift:hover { transform: translateY(-3px); box-shadow: var(--inkblot-shadow-md); }
|
|
188
|
+
.motion-box.press { transition: transform 80ms var(--inkblot-easing-default); }
|
|
189
|
+
.motion-box.press:active { transform: scale(0.96); }
|
|
190
|
+
.motion-box.scale-in { transition: transform var(--inkblot-duration-normal) var(--inkblot-easing-out), opacity var(--inkblot-duration-normal) var(--inkblot-easing-out); }
|
|
191
|
+
.motion-box.scale-in:hover { transform: scale(1.03); }
|
|
192
|
+
.motion-box.smooth { transition: transform var(--inkblot-duration-normal) var(--inkblot-easing-expressive); }
|
|
193
|
+
.motion-box.smooth:hover { transform: scale(1.04); }
|
|
194
|
+
.motion-box.arrive { transition: transform var(--inkblot-duration-slow) var(--inkblot-easing-expressive); }
|
|
195
|
+
.motion-box.arrive:hover { transform: translateY(-4px); }
|
|
196
|
+
.motion-box.fade { transition: opacity var(--inkblot-duration-normal) var(--inkblot-easing-out); opacity: 0.5; }
|
|
197
|
+
.motion-box.fade:hover { opacity: 1; }
|
|
198
|
+
|
|
199
|
+
/* EASING VIS */
|
|
200
|
+
.easing-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: var(--inkblot-spacing-4); }
|
|
201
|
+
.easing-item { padding: var(--inkblot-spacing-4); background: var(--inkblot-semantic-color-background-secondary); border-radius: var(--inkblot-radius-lg); }
|
|
202
|
+
.easing-name { font-size: 11px; font-weight: 600; margin-bottom: var(--inkblot-spacing-2); }
|
|
203
|
+
.easing-value { font-family: var(--inkblot-typography-font-family-mono); font-size: 10px; color: var(--inkblot-semantic-color-text-tertiary); margin-bottom: var(--inkblot-spacing-3); word-break: break-all; }
|
|
204
|
+
.easing-track { height: 4px; background: var(--inkblot-semantic-color-border-default); border-radius: 2px; position: relative; overflow: hidden; }
|
|
205
|
+
.easing-ball { width: 12px; height: 12px; border-radius: 50%; background: var(--inkblot-semantic-color-interactive-primary); position: absolute; top: -4px; left: 0; }
|
|
206
|
+
.easing-item:hover .easing-ball { animation: easingSlide 800ms forwards; }
|
|
207
|
+
@keyframes easingSlide { to { left: calc(100% - 12px); } }
|
|
208
|
+
.easing-item:nth-child(1):hover .easing-ball { animation-timing-function: cubic-bezier(0.25,0.1,0.25,1); }
|
|
209
|
+
.easing-item:nth-child(2):hover .easing-ball { animation-timing-function: cubic-bezier(0.32,0,0.67,0); }
|
|
210
|
+
.easing-item:nth-child(3):hover .easing-ball { animation-timing-function: cubic-bezier(0.33,1,0.68,1); }
|
|
211
|
+
.easing-item:nth-child(4):hover .easing-ball { animation-timing-function: cubic-bezier(0.65,0,0.35,1); }
|
|
212
|
+
.easing-item:nth-child(5):hover .easing-ball { animation-timing-function: cubic-bezier(0.22,1,0.36,1); }
|
|
213
|
+
|
|
214
|
+
/* RESPONSIVE */
|
|
215
|
+
.responsive-vis { display: grid; grid-template-columns: repeat(4, 1fr); gap: 2px; height: 120px; border-radius: var(--inkblot-radius-lg); overflow: hidden; }
|
|
216
|
+
.responsive-vis div { display: flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 500; color: white; }
|
|
217
|
+
|
|
218
|
+
/* STATE PRIORITY */
|
|
219
|
+
.state-table { width: 100%; border-collapse: collapse; font-size: var(--inkblot-typography-font-size-sm); }
|
|
220
|
+
.state-table th { text-align: left; font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--inkblot-semantic-color-text-tertiary); padding: var(--inkblot-spacing-2) var(--inkblot-spacing-3); border-bottom: 1px solid var(--inkblot-semantic-color-border-default); }
|
|
221
|
+
.state-table td { padding: var(--inkblot-spacing-2) var(--inkblot-spacing-3); border-bottom: 1px solid var(--inkblot-semantic-color-border-default); }
|
|
222
|
+
.state-table tr:last-child td { border-bottom: none; }
|
|
223
|
+
.state-table code { font-family: var(--inkblot-typography-font-family-mono); font-size: 11px; background: var(--inkblot-semantic-color-background-tertiary); padding: 2px 6px; border-radius: var(--inkblot-radius-sm); }
|
|
224
|
+
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
|
|
225
|
+
|
|
226
|
+
/* INTERACTION FLOW */
|
|
227
|
+
.flow-steps { display: flex; flex-direction: column; gap: var(--inkblot-spacing-2); counter-reset: step; }
|
|
228
|
+
.flow-step { display: flex; gap: var(--inkblot-spacing-3); align-items: flex-start; font-size: var(--inkblot-typography-font-size-sm); counter-increment: step; }
|
|
229
|
+
.flow-step::before { content: counter(step); flex-shrink: 0; width: 24px; height: 24px; background: var(--inkblot-semantic-color-interactive-primary); color: var(--inkblot-color-accent-citron-50); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 600; }
|
|
230
|
+
.flow-step span { padding-top: 2px; }
|
|
231
|
+
|
|
232
|
+
/* CONTENT RULES */
|
|
233
|
+
.truncate-1 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
234
|
+
.truncate-3 { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
|
|
235
|
+
.aspect-box { border-radius: var(--inkblot-radius-lg); overflow: hidden; background: linear-gradient(135deg, var(--inkblot-color-accent-citron-100), var(--inkblot-color-accent-citron-300)); display: flex; align-items: center; justify-content: center; color: var(--inkblot-color-accent-citron-700); font-size: 11px; font-weight: 600; }
|
|
236
|
+
|
|
237
|
+
/* MODAL DEMO */
|
|
238
|
+
.modal-overlay { display: none; position: fixed; inset: 0; background: rgba(29,28,25,0.45); z-index: 40; animation: fadeIn var(--inkblot-duration-normal) var(--inkblot-easing-out); }
|
|
239
|
+
.modal-overlay.open { display: flex; align-items: center; justify-content: center; }
|
|
240
|
+
.modal-dialog { background: var(--inkblot-semantic-color-background-secondary); border-radius: var(--inkblot-radius-xl); padding: var(--inkblot-spacing-8); width: 480px; max-width: 90vw; box-shadow: var(--inkblot-shadow-xl); animation: scaleIn var(--inkblot-duration-normal) var(--inkblot-easing-out); }
|
|
241
|
+
.modal-dialog h3 { font-size: var(--inkblot-typography-font-size-lg); font-weight: 600; margin-bottom: var(--inkblot-spacing-2); }
|
|
242
|
+
.modal-dialog p { font-size: var(--inkblot-typography-font-size-sm); color: var(--inkblot-semantic-color-text-secondary); margin-bottom: var(--inkblot-spacing-6); }
|
|
243
|
+
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
244
|
+
@keyframes scaleIn { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } }
|
|
245
|
+
|
|
246
|
+
/* TOOLTIP DEMO */
|
|
247
|
+
.tooltip-trigger { position: relative; display: inline-flex; }
|
|
248
|
+
.tooltip-trigger .tooltip-bubble { position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%) scale(0.96); background: var(--inkblot-semantic-color-background-inverse); color: var(--inkblot-semantic-color-text-inverse); padding: var(--inkblot-spacing-2) var(--inkblot-spacing-3); border-radius: var(--inkblot-radius-md); font-size: 12px; white-space: nowrap; pointer-events: none; opacity: 0; transition: opacity 150ms, transform 150ms; }
|
|
249
|
+
.tooltip-trigger:hover .tooltip-bubble, .tooltip-trigger:focus-within .tooltip-bubble { opacity: 1; transform: translateX(-50%) scale(1); }
|
|
250
|
+
|
|
251
|
+
/* PERF */
|
|
252
|
+
.perf-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--inkblot-spacing-4); }
|
|
253
|
+
.perf-item { padding: var(--inkblot-spacing-4); background: var(--inkblot-semantic-color-background-secondary); border-radius: var(--inkblot-radius-lg); }
|
|
254
|
+
.perf-item h4 { font-size: var(--inkblot-typography-font-size-sm); font-weight: 600; margin-bottom: var(--inkblot-spacing-2); }
|
|
255
|
+
.perf-item p { font-size: 12px; color: var(--inkblot-semantic-color-text-secondary); line-height: 1.5; }
|
|
256
|
+
|
|
257
|
+
/* ICON SEMANTICS */
|
|
258
|
+
.icon-table { width: 100%; border-collapse: collapse; font-size: var(--inkblot-typography-font-size-sm); }
|
|
259
|
+
.icon-table th { text-align: left; font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--inkblot-semantic-color-text-tertiary); padding: var(--inkblot-spacing-2) var(--inkblot-spacing-3); border-bottom: 1px solid var(--inkblot-semantic-color-border-default); }
|
|
260
|
+
.icon-table td { padding: var(--inkblot-spacing-2) var(--inkblot-spacing-3); border-bottom: 1px solid var(--inkblot-semantic-color-border-default); font-size: 13px; }
|
|
261
|
+
.icon-table tr:last-child td { border-bottom: none; }
|
|
262
|
+
.icon-table code { font-family: var(--inkblot-typography-font-family-mono); font-size: 11px; background: var(--inkblot-semantic-color-background-tertiary); padding: 2px 6px; border-radius: var(--inkblot-radius-sm); }
|
|
263
|
+
.icon-table .icon-preview { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: var(--inkblot-radius-md); background: var(--inkblot-semantic-color-background-secondary); }
|
|
264
|
+
.icon-table .icon-preview svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
|
|
265
|
+
.icon-size-demos { display: flex; align-items: end; gap: var(--inkblot-spacing-6); flex-wrap: wrap; }
|
|
266
|
+
.icon-size-demo { display: flex; flex-direction: column; align-items: center; gap: var(--inkblot-spacing-2); }
|
|
267
|
+
.icon-size-demo .icon-box { display: flex; align-items: center; justify-content: center; background: var(--inkblot-semantic-color-background-secondary); border-radius: var(--inkblot-radius-lg); }
|
|
268
|
+
.icon-size-demo .icon-box svg { stroke: var(--inkblot-semantic-color-interactive-primary); fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
|
|
269
|
+
.icon-size-demo span { font-size: 10px; color: var(--inkblot-semantic-color-text-tertiary); font-weight: 500; }
|
|
270
|
+
.icon-pos-demos { display: flex; flex-wrap: wrap; gap: var(--inkblot-spacing-4); }
|
|
271
|
+
.icon-pos-demo { display: inline-flex; align-items: center; gap: var(--inkblot-spacing-2); padding: var(--inkblot-spacing-2) var(--inkblot-spacing-4); background: var(--inkblot-semantic-color-background-secondary); border-radius: var(--inkblot-radius-lg); font-size: var(--inkblot-typography-font-size-sm); }
|
|
272
|
+
.icon-pos-demo svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
|
|
273
|
+
.icon-lib-badge { display: inline-flex; align-items: center; gap: var(--inkblot-spacing-2); padding: var(--inkblot-spacing-2) var(--inkblot-spacing-4); background: var(--inkblot-semantic-color-background-secondary); border-radius: var(--inkblot-radius-full); font-size: var(--inkblot-typography-font-size-sm); font-weight: 500; margin-bottom: var(--inkblot-spacing-6); }
|
|
274
|
+
@media (max-width: 768px) {
|
|
275
|
+
.icon-table td, .icon-table th { padding: var(--inkblot-spacing-2); font-size: 12px; }
|
|
276
|
+
.icon-table code { font-size: 10px; }
|
|
277
|
+
.icon-size-demos { gap: var(--inkblot-spacing-4); }
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.footer { text-align: center; padding: var(--inkblot-spacing-12) var(--inkblot-spacing-8); color: var(--inkblot-semantic-color-text-tertiary); font-size: var(--inkblot-typography-font-size-xs); border-top: 1px solid var(--inkblot-semantic-color-border-default); }
|
|
281
|
+
.footer code { background: var(--inkblot-semantic-color-background-secondary); padding: 2px 6px; border-radius: var(--inkblot-radius-sm); font-family: var(--inkblot-typography-font-family-mono); font-size: 11px; }
|
|
282
|
+
|
|
283
|
+
/* Mobile responsive */
|
|
284
|
+
@media (max-width: 768px) {
|
|
285
|
+
.nav { padding: var(--inkblot-spacing-3) var(--inkblot-spacing-4); flex-wrap: nowrap; position: relative; }
|
|
286
|
+
.nav-hamburger { display: flex; margin-left: auto; margin-right: var(--inkblot-spacing-2); }
|
|
287
|
+
.nav-menu { display: block; position: absolute; top: 100%; left: 0; right: 0; background: var(--inkblot-semantic-color-background-primary); border-bottom: 1px solid var(--inkblot-semantic-color-border-default); box-shadow: var(--inkblot-shadow-lg); max-height: 0; overflow: hidden; opacity: 0; transition: max-height 250ms cubic-bezier(0.25,0.1,0.25,1), opacity 200ms ease; z-index: 10; }
|
|
288
|
+
.nav.open .nav-menu { max-height: 80vh; overflow-y: auto; opacity: 1; }
|
|
289
|
+
.nav-links { flex-direction: column; gap: 0; padding: var(--inkblot-spacing-2) var(--inkblot-spacing-4) var(--inkblot-spacing-4); }
|
|
290
|
+
.nav-links a { display: block; font-size: var(--inkblot-typography-font-size-sm); padding: var(--inkblot-spacing-3) var(--inkblot-spacing-2); min-height: 44px; line-height: 1.4; white-space: normal; border-radius: var(--inkblot-radius-md); }
|
|
291
|
+
.nav-links a:hover { background: var(--inkblot-semantic-color-background-secondary); }
|
|
292
|
+
.container { padding: 0 var(--inkblot-spacing-4) var(--inkblot-spacing-16); }
|
|
293
|
+
.hero { padding: clamp(48px, 8vh, 96px) var(--inkblot-spacing-4); }
|
|
294
|
+
.hero h1 { font-size: clamp(2rem, 8vw, 2.5rem); }
|
|
295
|
+
.hero .tagline { font-size: var(--inkblot-typography-font-size-base); }
|
|
296
|
+
.hero-actions { gap: var(--inkblot-spacing-2); }
|
|
297
|
+
.hero-actions .btn { width: 100%; max-width: 280px; }
|
|
298
|
+
.card { padding: var(--inkblot-spacing-4); }
|
|
299
|
+
.section-header { margin-bottom: var(--inkblot-spacing-4); }
|
|
300
|
+
.section-title { font-size: var(--inkblot-typography-font-size-lg); }
|
|
301
|
+
.swatch-grid { grid-template-columns: repeat(auto-fill, minmax(64px, 1fr)); gap: var(--inkblot-spacing-1); }
|
|
302
|
+
.swatch-color { height: 48px; }
|
|
303
|
+
.swatch-meta { padding: 4px; font-size: 9px; }
|
|
304
|
+
.motion-grid { grid-template-columns: repeat(2, 1fr); gap: var(--inkblot-spacing-3); }
|
|
305
|
+
.motion-box { font-size: 10px; }
|
|
306
|
+
.easing-grid { grid-template-columns: 1fr 1fr; gap: var(--inkblot-spacing-3); }
|
|
307
|
+
.easing-value { font-size: 9px; }
|
|
308
|
+
.responsive-vis { grid-template-columns: 1fr; height: 80px; }
|
|
309
|
+
.responsive-vis div:not(:first-child) { display: none; }
|
|
310
|
+
.state-table { font-size: 12px; }
|
|
311
|
+
.state-table th, .state-table td { padding: var(--inkblot-spacing-2); }
|
|
312
|
+
.state-table code { font-size: 10px; padding: 1px 4px; }
|
|
313
|
+
.perf-grid { grid-template-columns: 1fr; }
|
|
314
|
+
.perf-item p { font-size: 11px; }
|
|
315
|
+
.modal-overlay.open { align-items: stretch; }
|
|
316
|
+
.modal-dialog { width: 100%; max-width: none; height: 100%; max-height: 100vh; margin: 0; padding: var(--inkblot-spacing-6); border-radius: 0; }
|
|
317
|
+
.footer { padding: var(--inkblot-spacing-8) var(--inkblot-spacing-4); }
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
@media (max-width: 480px) {
|
|
321
|
+
.nav { padding: var(--inkblot-spacing-2) var(--inkblot-spacing-3); }
|
|
322
|
+
.nav-brand { font-size: var(--inkblot-typography-font-size-xs); }
|
|
323
|
+
.container { padding: 0 var(--inkblot-spacing-3) var(--inkblot-spacing-12); }
|
|
324
|
+
.section { margin-bottom: var(--inkblot-spacing-12); }
|
|
325
|
+
.section-title { font-size: var(--inkblot-typography-font-size-md); }
|
|
326
|
+
.card { padding: var(--inkblot-spacing-3); border-radius: var(--inkblot-radius-lg); }
|
|
327
|
+
.btn { padding: var(--inkblot-spacing-2) var(--inkblot-spacing-4); min-height: 44px; }
|
|
328
|
+
.btn-lg { padding: var(--inkblot-spacing-3) var(--inkblot-spacing-6); }
|
|
329
|
+
.swatch-grid { grid-template-columns: repeat(3, 1fr); }
|
|
330
|
+
.swatch-color { height: 44px; }
|
|
331
|
+
.motion-grid { grid-template-columns: repeat(2, 1fr); gap: var(--inkblot-spacing-2); }
|
|
332
|
+
.easing-grid { grid-template-columns: 1fr; }
|
|
333
|
+
.flow-step { font-size: 12px; }
|
|
334
|
+
.flow-step::before { width: 20px; height: 20px; font-size: 10px; }
|
|
335
|
+
.radius-box { width: 56px; height: 56px; font-size: 10px; }
|
|
336
|
+
.shadow-box { width: 72px; height: 72px; font-size: 10px; }
|
|
337
|
+
.scale-box { font-size: 9px; }
|
|
338
|
+
.empty-state { padding: var(--inkblot-spacing-8) var(--inkblot-spacing-3); }
|
|
339
|
+
.empty-state h3 { font-size: var(--inkblot-typography-font-size-md); }
|
|
340
|
+
.truncate-3 { max-width: 100% !important; }
|
|
341
|
+
.aspect-box { font-size: 10px; }
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
@media (max-width: 375px) {
|
|
345
|
+
.swatch-grid { grid-template-columns: repeat(2, 1fr); }
|
|
346
|
+
.hero-actions .btn { font-size: var(--inkblot-typography-font-size-sm); }
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/* Prevent horizontal overflow */
|
|
350
|
+
html, body { overflow-x: hidden; }
|
|
351
|
+
.container, .card, .section-header, .nav { max-width: 100%; }
|
|
352
|
+
|
|
353
|
+
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } }
|
|
354
|
+
</style>
|
|
355
|
+
</head>
|
|
356
|
+
<body>
|
|
357
|
+
<nav class="nav">
|
|
358
|
+
<a href="#" class="nav-brand" onclick="window.scrollTo({top:0,behavior:'smooth'});return false">Citron</a>
|
|
359
|
+
<button class="nav-hamburger" type="button" aria-label="Open menu" aria-expanded="false" aria-controls="nav-menu" id="nav-hamburger"><span></span></button>
|
|
360
|
+
<div class="nav-menu" id="nav-menu">
|
|
361
|
+
<ul class="nav-links">
|
|
362
|
+
<li><a href="#colors">Colors</a></li>
|
|
363
|
+
<li><a href="#typography">Type</a></li>
|
|
364
|
+
<li><a href="#components">Components</a></li>
|
|
365
|
+
<li><a href="#motion">Motion</a></li>
|
|
366
|
+
<li><a href="#icons">Icons</a></li>
|
|
367
|
+
<li><a href="#responsive">Responsive</a></li>
|
|
368
|
+
<li><a href="#states-multi">States</a></li>
|
|
369
|
+
<li><a href="#interactions">Flows</a></li>
|
|
370
|
+
<li><a href="#content">Content</a></li>
|
|
371
|
+
<li><a href="#performance">Perf</a></li>
|
|
372
|
+
<li><a href="#spacing">Spacing</a></li>
|
|
373
|
+
<li><a href="#elevation">Elevation</a></li>
|
|
374
|
+
</ul>
|
|
375
|
+
</div>
|
|
376
|
+
<button class="theme-toggle" aria-label="Toggle dark mode" id="theme-toggle"></button>
|
|
377
|
+
</nav>
|
|
378
|
+
|
|
379
|
+
<header class="hero">
|
|
380
|
+
<h1>Citron Design System</h1>
|
|
381
|
+
<p class="tagline">Warmly minimal. Quietly distinctive. Tokens, components, and rules crafted with restraint — built for AI agents and humans alike.</p>
|
|
382
|
+
<div class="hero-actions">
|
|
383
|
+
<button class="btn btn-primary btn-lg" onclick="document.getElementById('components').scrollIntoView({behavior:'smooth'})">Get started</button>
|
|
384
|
+
<button class="btn btn-secondary btn-lg" onclick="document.getElementById('colors').scrollIntoView({behavior:'smooth'})">View tokens</button>
|
|
385
|
+
</div>
|
|
386
|
+
</header>
|
|
387
|
+
|
|
388
|
+
<main class="container">
|
|
389
|
+
|
|
390
|
+
<!-- COLORS -->
|
|
391
|
+
<section class="section" id="colors">
|
|
392
|
+
<div class="section-header"><h2 class="section-title">Color</h2><p class="section-desc">Warm neutrals · Citron accent · Status · Click any swatch to copy token</p></div>
|
|
393
|
+
<div class="two-col">
|
|
394
|
+
<div>
|
|
395
|
+
<div class="card-title">Warm Neutrals</div>
|
|
396
|
+
<div class="swatch-grid" id="neutral-swatches">
|
|
397
|
+
<div class="swatch" data-hex="#ffffff" data-token="--inkblot-color-neutral-white"><div class="swatch-color" style="background:#fff;border:1px solid var(--inkblot-semantic-color-border-default)"></div><div class="swatch-meta">white</div></div>
|
|
398
|
+
<div class="swatch" data-hex="#fafaf7" data-token="--inkblot-color-neutral-gray-50"><div class="swatch-color" style="background:var(--inkblot-color-neutral-gray-50)"></div><div class="swatch-meta">50</div></div>
|
|
399
|
+
<div class="swatch" data-hex="#f5f4f0" data-token="--inkblot-color-neutral-gray-100"><div class="swatch-color" style="background:var(--inkblot-color-neutral-gray-100)"></div><div class="swatch-meta">100</div></div>
|
|
400
|
+
<div class="swatch" data-hex="#eae9e3" data-token="--inkblot-color-neutral-gray-200"><div class="swatch-color" style="background:var(--inkblot-color-neutral-gray-200)"></div><div class="swatch-meta">200</div></div>
|
|
401
|
+
<div class="swatch" data-hex="#d4d3cb" data-token="--inkblot-color-neutral-gray-300"><div class="swatch-color" style="background:var(--inkblot-color-neutral-gray-300)"></div><div class="swatch-meta">300</div></div>
|
|
402
|
+
<div class="swatch" data-hex="#93928a" data-token="--inkblot-color-neutral-gray-400"><div class="swatch-color" style="background:var(--inkblot-color-neutral-gray-400)"></div><div class="swatch-meta">400</div></div>
|
|
403
|
+
<div class="swatch" data-hex="#6b6a63" data-token="--inkblot-color-neutral-gray-500"><div class="swatch-color" style="background:var(--inkblot-color-neutral-gray-500)"></div><div class="swatch-meta">500</div></div>
|
|
404
|
+
<div class="swatch" data-hex="#4d4c47" data-token="--inkblot-color-neutral-gray-600"><div class="swatch-color" style="background:var(--inkblot-color-neutral-gray-600)"></div><div class="swatch-meta">600</div></div>
|
|
405
|
+
<div class="swatch" data-hex="#3a3935" data-token="--inkblot-color-neutral-gray-700"><div class="swatch-color" style="background:var(--inkblot-color-neutral-gray-700)"></div><div class="swatch-meta">700</div></div>
|
|
406
|
+
<div class="swatch" data-hex="#2d2c28" data-token="--inkblot-color-neutral-gray-800"><div class="swatch-color" style="background:var(--inkblot-color-neutral-gray-800)"></div><div class="swatch-meta">800</div></div>
|
|
407
|
+
<div class="swatch" data-hex="#1d1c19" data-token="--inkblot-color-neutral-gray-900"><div class="swatch-color" style="background:var(--inkblot-color-neutral-gray-900)"></div><div class="swatch-meta">900</div></div>
|
|
408
|
+
<div class="swatch" data-hex="#0f0e0c" data-token="--inkblot-color-neutral-gray-950"><div class="swatch-color" style="background:var(--inkblot-color-neutral-gray-950)"></div><div class="swatch-meta">950</div></div>
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
411
|
+
<div>
|
|
412
|
+
<div class="card-title">Citron Accent</div>
|
|
413
|
+
<div class="swatch-grid">
|
|
414
|
+
<div class="swatch" data-hex="#fdfbf3" data-token="--inkblot-color-accent-citron-50"><div class="swatch-color" style="background:var(--inkblot-color-accent-citron-50)"></div><div class="swatch-meta">50</div></div>
|
|
415
|
+
<div class="swatch" data-hex="#faf4df" data-token="--inkblot-color-accent-citron-100"><div class="swatch-color" style="background:var(--inkblot-color-accent-citron-100)"></div><div class="swatch-meta">100</div></div>
|
|
416
|
+
<div class="swatch" data-hex="#f3e6b8" data-token="--inkblot-color-accent-citron-200"><div class="swatch-color" style="background:var(--inkblot-color-accent-citron-200)"></div><div class="swatch-meta">200</div></div>
|
|
417
|
+
<div class="swatch" data-hex="#e8d38a" data-token="--inkblot-color-accent-citron-300"><div class="swatch-color" style="background:var(--inkblot-color-accent-citron-300)"></div><div class="swatch-meta">300</div></div>
|
|
418
|
+
<div class="swatch" data-hex="#d9bc58" data-token="--inkblot-color-accent-citron-400"><div class="swatch-color" style="background:var(--inkblot-color-accent-citron-400)"></div><div class="swatch-meta">400</div></div>
|
|
419
|
+
<div class="swatch" data-hex="#c4a030" data-token="--inkblot-color-accent-citron-500"><div class="swatch-color" style="background:var(--inkblot-color-accent-citron-500)"></div><div class="swatch-meta">500</div></div>
|
|
420
|
+
<div class="swatch" data-hex="#a38427" data-token="--inkblot-color-accent-citron-600"><div class="swatch-color" style="background:var(--inkblot-color-accent-citron-600)"></div><div class="swatch-meta">600</div></div>
|
|
421
|
+
<div class="swatch" data-hex="#7f671f" data-token="--inkblot-color-accent-citron-700"><div class="swatch-color" style="background:var(--inkblot-color-accent-citron-700)"></div><div class="swatch-meta">700</div></div>
|
|
422
|
+
<div class="swatch" data-hex="#5c4a18" data-token="--inkblot-color-accent-citron-800"><div class="swatch-color" style="background:var(--inkblot-color-accent-citron-800)"></div><div class="swatch-meta">800</div></div>
|
|
423
|
+
<div class="swatch" data-hex="#3d3111" data-token="--inkblot-color-accent-citron-900"><div class="swatch-color" style="background:var(--inkblot-color-accent-citron-900)"></div><div class="swatch-meta">900</div></div>
|
|
424
|
+
</div>
|
|
425
|
+
<div class="card-title" style="margin-top: var(--inkblot-spacing-6)">Status</div>
|
|
426
|
+
<div class="swatch-grid" style="max-width: 260px">
|
|
427
|
+
<div class="swatch" data-hex="#358c46" data-token="--inkblot-semantic-color-status-success"><div class="swatch-color" style="background:var(--inkblot-semantic-color-status-success)"></div><div class="swatch-meta">success</div></div>
|
|
428
|
+
<div class="swatch" data-hex="#c48c1a" data-token="--inkblot-semantic-color-status-warning"><div class="swatch-color" style="background:var(--inkblot-semantic-color-status-warning)"></div><div class="swatch-meta">warning</div></div>
|
|
429
|
+
<div class="swatch" data-hex="#be3e35" data-token="--inkblot-semantic-color-status-error"><div class="swatch-color" style="background:var(--inkblot-semantic-color-status-error)"></div><div class="swatch-meta">error</div></div>
|
|
430
|
+
<div class="swatch" data-hex="#5790ad" data-token="--inkblot-semantic-color-status-info"><div class="swatch-color" style="background:var(--inkblot-semantic-color-status-info)"></div><div class="swatch-meta">info</div></div>
|
|
431
|
+
</div>
|
|
432
|
+
<div class="badges" style="margin-top: var(--inkblot-spacing-4)">
|
|
433
|
+
<span class="badge badge-success">Success</span><span class="badge badge-warning">Warning</span><span class="badge badge-error">Error</span><span class="badge badge-info">Info</span>
|
|
434
|
+
</div>
|
|
435
|
+
</div>
|
|
436
|
+
</div>
|
|
437
|
+
</section>
|
|
438
|
+
|
|
439
|
+
<!-- TYPOGRAPHY -->
|
|
440
|
+
<section class="section" id="typography">
|
|
441
|
+
<div class="section-header"><h2 class="section-title">Typography</h2><p class="section-desc">Inter · Humanist sans-serif · Restrained scale · Intentional hierarchy</p></div>
|
|
442
|
+
<div class="type-row"><div class="meta">DISPLAY · 52px</div><div style="font-size:var(--inkblot-typography-font-size-5xl);font-weight:600;letter-spacing:-0.03em;line-height:1.15">Citron</div></div>
|
|
443
|
+
<div class="type-row"><div class="meta">HEADING 1 · 34px</div><div style="font:var(--inkblot-semantic-typography-heading-1)">Designed with restraint</div></div>
|
|
444
|
+
<div class="type-row"><div class="meta">HEADING 2 · 28px</div><div style="font:var(--inkblot-semantic-typography-heading-2)">Every detail considered</div></div>
|
|
445
|
+
<div class="type-row"><div class="meta">HEADING 3 · 24px</div><div style="font:var(--inkblot-semantic-typography-heading-3)">Components at scale</div></div>
|
|
446
|
+
<div class="type-row"><div class="meta">BODY · 15px</div><div style="font:var(--inkblot-semantic-typography-body-default);max-width:600px">The quick brown fox jumps over the lazy dog. Warm, readable, human typography.</div></div>
|
|
447
|
+
<div class="type-row"><div class="meta">SMALL · 13px</div><div style="font:var(--inkblot-semantic-typography-body-small);color:var(--inkblot-semantic-color-text-secondary)">Caption text for metadata, timestamps, secondary information.</div></div>
|
|
448
|
+
<div class="type-row"><div class="meta">LABEL · 12px</div><div style="font:var(--inkblot-semantic-typography-label-default);text-transform:uppercase;color:var(--inkblot-semantic-color-text-tertiary)">Form label</div></div>
|
|
449
|
+
<div class="type-row"><div class="meta">MONO</div><div style="font-family:var(--inkblot-typography-font-family-mono);font-size:var(--inkblot-typography-font-size-sm);color:var(--inkblot-semantic-color-text-secondary)">var(--inkblot-spacing-4)</div></div>
|
|
450
|
+
</section>
|
|
451
|
+
|
|
452
|
+
<!-- COMPONENTS -->
|
|
453
|
+
<section class="section" id="components">
|
|
454
|
+
<div class="section-header"><h2 class="section-title">Components</h2><p class="section-desc">Buttons · Inputs · Toggles · Toasts · Modal · Tooltip</p></div>
|
|
455
|
+
<div class="two-col">
|
|
456
|
+
<div class="card">
|
|
457
|
+
<div class="card-title">Buttons</div>
|
|
458
|
+
<div style="display:flex;flex-direction:column;gap:var(--inkblot-spacing-4)">
|
|
459
|
+
<div style="display:flex;gap:var(--inkblot-spacing-3);flex-wrap:wrap;align-items:center"><button class="btn btn-primary btn-sm">Small</button><button class="btn btn-primary">Default</button><button class="btn btn-primary btn-lg">Large</button></div>
|
|
460
|
+
<div style="display:flex;gap:var(--inkblot-spacing-3);flex-wrap:wrap"><button class="btn btn-primary">Primary</button><button class="btn btn-secondary">Secondary</button><button class="btn btn-ghost">Ghost</button><button class="btn btn-danger">Danger</button></div>
|
|
461
|
+
<div style="display:flex;gap:var(--inkblot-spacing-3);align-items:center">
|
|
462
|
+
<button class="btn btn-primary" disabled style="opacity:var(--inkblot-opacity-disabled)">Disabled</button>
|
|
463
|
+
<span class="tooltip-trigger"><button class="btn btn-secondary">Hover me</button><span class="tooltip-bubble">Tooltip appears on hover</span></span>
|
|
464
|
+
</div>
|
|
465
|
+
</div>
|
|
466
|
+
</div>
|
|
467
|
+
<div class="card">
|
|
468
|
+
<div class="card-title">Form inputs</div>
|
|
469
|
+
<div style="display:flex;flex-direction:column;gap:var(--inkblot-spacing-4)">
|
|
470
|
+
<div class="input-group"><label class="input-label">Email address</label><input class="input" type="email" placeholder="you@example.com" /><span class="input-help">We'll never share your email.</span></div>
|
|
471
|
+
<div class="input-group"><label class="input-label">Password</label><input class="input input-error" type="password" value="short" /><span class="input-error-msg">Password must be at least 8 characters.</span></div>
|
|
472
|
+
<div style="display:flex;align-items:center;gap:var(--inkblot-spacing-3)">
|
|
473
|
+
<button class="toggle-track on" role="switch" aria-checked="true" onclick="this.classList.toggle('on');this.setAttribute('aria-checked',this.classList.contains('on'))"><span class="toggle-thumb"></span></button>
|
|
474
|
+
<span style="font-size:var(--inkblot-typography-font-size-sm)">Enable notifications</span>
|
|
475
|
+
</div>
|
|
476
|
+
</div>
|
|
477
|
+
</div>
|
|
478
|
+
</div>
|
|
479
|
+
<div class="two-col" style="margin-top:var(--inkblot-spacing-6)">
|
|
480
|
+
<div class="card">
|
|
481
|
+
<div class="card-title">Toast notifications</div>
|
|
482
|
+
<div style="display:flex;flex-direction:column;gap:var(--inkblot-spacing-3)">
|
|
483
|
+
<div class="toast toast-success"><span class="toast-dot"></span>Account created successfully.</div>
|
|
484
|
+
<div class="toast toast-info"><span class="toast-dot"></span>Your changes have been saved.</div>
|
|
485
|
+
<div class="toast toast-warning"><span class="toast-dot"></span>Storage is almost full.</div>
|
|
486
|
+
<div class="toast toast-error"><span class="toast-dot"></span>Unable to connect. Check your network.</div>
|
|
487
|
+
</div>
|
|
488
|
+
</div>
|
|
489
|
+
<div class="card">
|
|
490
|
+
<div class="card-title">Modal & Tooltip</div>
|
|
491
|
+
<p style="font-size:var(--inkblot-typography-font-size-sm);color:var(--inkblot-semantic-color-text-secondary);margin-bottom:var(--inkblot-spacing-4)">Interactive demos of focus-trapping modal and hover tooltip.</p>
|
|
492
|
+
<div style="display:flex;gap:var(--inkblot-spacing-3);flex-wrap:wrap">
|
|
493
|
+
<button class="btn btn-primary" id="open-modal">Open modal</button>
|
|
494
|
+
<span class="tooltip-trigger"><button class="btn btn-secondary" tabindex="0">Tooltip demo</button><span class="tooltip-bubble">I appear after 0ms (CSS)</span></span>
|
|
495
|
+
</div>
|
|
496
|
+
</div>
|
|
497
|
+
</div>
|
|
498
|
+
</section>
|
|
499
|
+
|
|
500
|
+
<!-- MOTION -->
|
|
501
|
+
<section class="section" id="motion">
|
|
502
|
+
<div class="section-header"><h2 class="section-title">Motion & Easing</h2><p class="section-desc">Physical, smooth, fast. No bounce. No overshoot. Light responding to touch.</p></div>
|
|
503
|
+
<div class="card-title">Micro-interactions</div>
|
|
504
|
+
<div class="motion-grid" style="margin-bottom:var(--inkblot-spacing-8)">
|
|
505
|
+
<div class="motion-item"><div class="motion-box hover-lift">Lift</div><div class="motion-label">120ms · default</div></div>
|
|
506
|
+
<div class="motion-item"><div class="motion-box press">Press</div><div class="motion-label">80ms · physical</div></div>
|
|
507
|
+
<div class="motion-item"><div class="motion-box scale-in">Scale</div><div class="motion-label">200ms · ease-out</div></div>
|
|
508
|
+
<div class="motion-item"><div class="motion-box smooth">Smooth</div><div class="motion-label">200ms · expressive</div></div>
|
|
509
|
+
<div class="motion-item"><div class="motion-box arrive">Arrive</div><div class="motion-label">320ms · expressive</div></div>
|
|
510
|
+
<div class="motion-item"><div class="motion-box fade">Fade</div><div class="motion-label">200ms · opacity</div></div>
|
|
511
|
+
</div>
|
|
512
|
+
<div class="card-title">Easing curves</div>
|
|
513
|
+
<p style="font-size:var(--inkblot-typography-font-size-sm);color:var(--inkblot-semantic-color-text-secondary);margin-bottom:var(--inkblot-spacing-4)">Hover each to see the ball animate across the track.</p>
|
|
514
|
+
<div class="easing-grid">
|
|
515
|
+
<div class="easing-item"><div class="easing-name">default</div><div class="easing-value">cubic-bezier(0.25, 0.1, 0.25, 1)</div><div class="easing-track"><div class="easing-ball"></div></div></div>
|
|
516
|
+
<div class="easing-item"><div class="easing-name">ease-in</div><div class="easing-value">cubic-bezier(0.32, 0, 0.67, 0)</div><div class="easing-track"><div class="easing-ball"></div></div></div>
|
|
517
|
+
<div class="easing-item"><div class="easing-name">ease-out</div><div class="easing-value">cubic-bezier(0.33, 1, 0.68, 1)</div><div class="easing-track"><div class="easing-ball"></div></div></div>
|
|
518
|
+
<div class="easing-item"><div class="easing-name">ease-in-out</div><div class="easing-value">cubic-bezier(0.65, 0, 0.35, 1)</div><div class="easing-track"><div class="easing-ball"></div></div></div>
|
|
519
|
+
<div class="easing-item"><div class="easing-name">expressive</div><div class="easing-value">cubic-bezier(0.22, 1, 0.36, 1)</div><div class="easing-track"><div class="easing-ball"></div></div></div>
|
|
520
|
+
</div>
|
|
521
|
+
</section>
|
|
522
|
+
|
|
523
|
+
<!-- ICON SEMANTICS -->
|
|
524
|
+
<section class="section" id="icons">
|
|
525
|
+
<div class="section-header"><h2 class="section-title">Icon Semantics</h2><p class="section-desc">Lucide icons · Semantic mapping · Component-specific · Sizes · Positioning</p></div>
|
|
526
|
+
<div class="icon-lib-badge">
|
|
527
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>
|
|
528
|
+
Library: <strong>Lucide</strong> — free, ISC license, 1500+ icons
|
|
529
|
+
</div>
|
|
530
|
+
|
|
531
|
+
<div class="two-col" style="margin-bottom:var(--inkblot-spacing-8)">
|
|
532
|
+
<div class="card">
|
|
533
|
+
<div class="card-title">Status icons</div>
|
|
534
|
+
<div class="table-scroll">
|
|
535
|
+
<table class="icon-table">
|
|
536
|
+
<thead><tr><th>Meaning</th><th>Icon</th><th>Lucide name</th></tr></thead>
|
|
537
|
+
<tbody>
|
|
538
|
+
<tr><td>Success</td><td><span class="icon-preview" style="color:var(--inkblot-semantic-color-status-success)"><svg viewBox="0 0 24 24"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg></span></td><td><code>check-circle</code></td></tr>
|
|
539
|
+
<tr><td>Error</td><td><span class="icon-preview" style="color:var(--inkblot-semantic-color-status-error)"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg></span></td><td><code>x-circle</code></td></tr>
|
|
540
|
+
<tr><td>Warning</td><td><span class="icon-preview" style="color:var(--inkblot-semantic-color-status-warning)"><svg viewBox="0 0 24 24"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg></span></td><td><code>alert-triangle</code></td></tr>
|
|
541
|
+
<tr><td>Info</td><td><span class="icon-preview" style="color:var(--inkblot-semantic-color-status-info)"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg></span></td><td><code>info</code></td></tr>
|
|
542
|
+
<tr><td>Loading</td><td><span class="icon-preview"><svg viewBox="0 0 24 24" style="animation:spin 1s linear infinite"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg></span></td><td><code>loader-2</code></td></tr>
|
|
543
|
+
<tr><td>Pending</td><td><span class="icon-preview" style="color:var(--inkblot-semantic-color-text-tertiary)"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg></span></td><td><code>clock</code></td></tr>
|
|
544
|
+
</tbody>
|
|
545
|
+
</table>
|
|
546
|
+
</div>
|
|
547
|
+
</div>
|
|
548
|
+
<div class="card">
|
|
549
|
+
<div class="card-title">Action icons</div>
|
|
550
|
+
<div class="table-scroll">
|
|
551
|
+
<table class="icon-table">
|
|
552
|
+
<thead><tr><th>Action</th><th>Icon</th><th>Lucide name</th></tr></thead>
|
|
553
|
+
<tbody>
|
|
554
|
+
<tr><td>Close</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg></span></td><td><code>x</code></td></tr>
|
|
555
|
+
<tr><td>Add</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><path d="M5 12h14"/><path d="M12 5v14"/></svg></span></td><td><code>plus</code></td></tr>
|
|
556
|
+
<tr><td>Delete</td><td><span class="icon-preview" style="color:var(--inkblot-semantic-color-status-error)"><svg viewBox="0 0 24 24"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/></svg></span></td><td><code>trash-2</code></td></tr>
|
|
557
|
+
<tr><td>Edit</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg></span></td><td><code>pencil</code></td></tr>
|
|
558
|
+
<tr><td>Save</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg></span></td><td><code>save</code></td></tr>
|
|
559
|
+
<tr><td>Search</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg></span></td><td><code>search</code></td></tr>
|
|
560
|
+
<tr><td>Share</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" x2="15.42" y1="13.51" y2="17.49"/><line x1="15.41" x2="8.59" y1="6.51" y2="10.49"/></svg></span></td><td><code>share-2</code></td></tr>
|
|
561
|
+
<tr><td>Download</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg></span></td><td><code>download</code></td></tr>
|
|
562
|
+
<tr><td>Settings</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg></span></td><td><code>settings</code></td></tr>
|
|
563
|
+
</tbody>
|
|
564
|
+
</table>
|
|
565
|
+
</div>
|
|
566
|
+
</div>
|
|
567
|
+
</div>
|
|
568
|
+
|
|
569
|
+
<div class="two-col" style="margin-bottom:var(--inkblot-spacing-8)">
|
|
570
|
+
<div class="card">
|
|
571
|
+
<div class="card-title">Entity icons</div>
|
|
572
|
+
<div class="table-scroll">
|
|
573
|
+
<table class="icon-table">
|
|
574
|
+
<thead><tr><th>Entity</th><th>Icon</th><th>Lucide name</th></tr></thead>
|
|
575
|
+
<tbody>
|
|
576
|
+
<tr><td>User</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg></span></td><td><code>user</code></td></tr>
|
|
577
|
+
<tr><td>Notification</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg></span></td><td><code>bell</code></td></tr>
|
|
578
|
+
<tr><td>Email</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg></span></td><td><code>mail</code></td></tr>
|
|
579
|
+
<tr><td>File</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"/><polyline points="14 2 14 8 20 8"/></svg></span></td><td><code>file</code></td></tr>
|
|
580
|
+
<tr><td>Folder</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><path d="M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z"/></svg></span></td><td><code>folder</code></td></tr>
|
|
581
|
+
<tr><td>Calendar</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><rect width="18" height="18" x="3" y="4" rx="2" ry="2"/><line x1="16" x2="16" y1="2" y2="6"/><line x1="8" x2="8" y1="2" y2="6"/><line x1="3" x2="21" y1="10" y2="10"/></svg></span></td><td><code>calendar</code></td></tr>
|
|
582
|
+
<tr><td>Location</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/><circle cx="12" cy="10" r="3"/></svg></span></td><td><code>map-pin</code></td></tr>
|
|
583
|
+
</tbody>
|
|
584
|
+
</table>
|
|
585
|
+
</div>
|
|
586
|
+
</div>
|
|
587
|
+
<div class="card">
|
|
588
|
+
<div class="card-title">Navigation icons</div>
|
|
589
|
+
<div class="table-scroll">
|
|
590
|
+
<table class="icon-table">
|
|
591
|
+
<thead><tr><th>Purpose</th><th>Icon</th><th>Lucide name</th></tr></thead>
|
|
592
|
+
<tbody>
|
|
593
|
+
<tr><td>Menu</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><line x1="4" x2="20" y1="12" y2="12"/><line x1="4" x2="20" y1="6" y2="6"/><line x1="4" x2="20" y1="18" y2="18"/></svg></span></td><td><code>menu</code></td></tr>
|
|
594
|
+
<tr><td>Back</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><path d="m12 19-7-7 7-7"/><path d="M19 12H5"/></svg></span></td><td><code>arrow-left</code></td></tr>
|
|
595
|
+
<tr><td>Forward</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg></span></td><td><code>arrow-right</code></td></tr>
|
|
596
|
+
<tr><td>Home</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg></span></td><td><code>home</code></td></tr>
|
|
597
|
+
<tr><td>Expand</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><path d="m6 9 6 6 6-6"/></svg></span></td><td><code>chevron-down</code></td></tr>
|
|
598
|
+
<tr><td>Disclosure</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><path d="m9 18 6-6-6-6"/></svg></span></td><td><code>chevron-right</code></td></tr>
|
|
599
|
+
<tr><td>External link</td><td><span class="icon-preview"><svg viewBox="0 0 24 24"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" x2="21" y1="14" y2="3"/></svg></span></td><td><code>external-link</code></td></tr>
|
|
600
|
+
</tbody>
|
|
601
|
+
</table>
|
|
602
|
+
</div>
|
|
603
|
+
</div>
|
|
604
|
+
</div>
|
|
605
|
+
|
|
606
|
+
<div class="card-title">Sizes</div>
|
|
607
|
+
<p style="font-size:var(--inkblot-typography-font-size-sm);color:var(--inkblot-semantic-color-text-secondary);margin-bottom:var(--inkblot-spacing-4)">6 standard sizes mapped to design tokens. AI agents: always use the token, never hard-code pixels.</p>
|
|
608
|
+
<div class="icon-size-demos" style="margin-bottom:var(--inkblot-spacing-8)">
|
|
609
|
+
<div class="icon-size-demo"><div class="icon-box" style="width:28px;height:28px"><svg width="12" height="12" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg></div><span>xs · 12px</span></div>
|
|
610
|
+
<div class="icon-size-demo"><div class="icon-box" style="width:36px;height:36px"><svg width="16" height="16" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg></div><span>sm · 16px</span></div>
|
|
611
|
+
<div class="icon-size-demo"><div class="icon-box" style="width:40px;height:40px"><svg width="20" height="20" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg></div><span>md · 20px</span></div>
|
|
612
|
+
<div class="icon-size-demo"><div class="icon-box" style="width:48px;height:48px"><svg width="24" height="24" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg></div><span>lg · 24px</span></div>
|
|
613
|
+
<div class="icon-size-demo"><div class="icon-box" style="width:56px;height:56px"><svg width="32" height="32" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg></div><span>xl · 32px</span></div>
|
|
614
|
+
<div class="icon-size-demo"><div class="icon-box" style="width:72px;height:72px"><svg width="48" height="48" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg></div><span>2xl · 48px</span></div>
|
|
615
|
+
</div>
|
|
616
|
+
|
|
617
|
+
<div class="card-title">Positioning</div>
|
|
618
|
+
<p style="font-size:var(--inkblot-typography-font-size-sm);color:var(--inkblot-semantic-color-text-secondary);margin-bottom:var(--inkblot-spacing-4)">4 standard positions. Leading = before text (default). Trailing = after text. Standalone = no text (needs aria-label). Inset = inside form fields.</p>
|
|
619
|
+
<div class="icon-pos-demos" style="margin-bottom:var(--inkblot-spacing-8)">
|
|
620
|
+
<div class="icon-pos-demo"><svg viewBox="0 0 24 24"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg> Leading</div>
|
|
621
|
+
<div class="icon-pos-demo">Trailing <svg viewBox="0 0 24 24"><path d="m9 18 6-6-6-6"/></svg></div>
|
|
622
|
+
<div class="icon-pos-demo" style="padding:var(--inkblot-spacing-2);width:44px;height:44px;justify-content:center"><svg viewBox="0 0 24 24"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg></div>
|
|
623
|
+
<div class="icon-pos-demo"><svg viewBox="0 0 24 24" style="color:var(--inkblot-semantic-color-text-tertiary)"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg><span style="color:var(--inkblot-semantic-color-text-tertiary)">Inset in field</span></div>
|
|
624
|
+
</div>
|
|
625
|
+
|
|
626
|
+
<div class="two-col">
|
|
627
|
+
<div class="card">
|
|
628
|
+
<div class="card-title">Component-specific icons</div>
|
|
629
|
+
<div class="table-scroll">
|
|
630
|
+
<table class="icon-table">
|
|
631
|
+
<thead><tr><th>Component</th><th>Context</th><th>Icon</th></tr></thead>
|
|
632
|
+
<tbody>
|
|
633
|
+
<tr><td>Button</td><td>Submit</td><td><code>check</code> (leading)</td></tr>
|
|
634
|
+
<tr><td>Button</td><td>Cancel</td><td><code>x</code> (leading)</td></tr>
|
|
635
|
+
<tr><td>Button</td><td>Loading</td><td><code>loader-2</code> (spin)</td></tr>
|
|
636
|
+
<tr><td>Input</td><td>Email</td><td><code>mail</code> (inset-leading)</td></tr>
|
|
637
|
+
<tr><td>Input</td><td>Password</td><td><code>lock</code> + <code>eye</code>/<code>eye-off</code></td></tr>
|
|
638
|
+
<tr><td>Input</td><td>Search</td><td><code>search</code> + <code>x</code> (clear)</td></tr>
|
|
639
|
+
<tr><td>Select</td><td>Trigger</td><td><code>chevron-down</code> (rotate 180°)</td></tr>
|
|
640
|
+
<tr><td>Modal</td><td>Close</td><td><code>x</code> (standalone, top-right)</td></tr>
|
|
641
|
+
<tr><td>Toast</td><td>Dismiss</td><td><code>x</code> (sm, trailing)</td></tr>
|
|
642
|
+
<tr><td>Table</td><td>Sort</td><td><code>arrow-up-down</code> / <code>arrow-up</code> / <code>arrow-down</code></td></tr>
|
|
643
|
+
<tr><td>List item</td><td>Disclosure</td><td><code>chevron-right</code> (trailing)</td></tr>
|
|
644
|
+
<tr><td>Chip</td><td>Remove</td><td><code>x</code> (sm, trailing)</td></tr>
|
|
645
|
+
<tr><td>Empty state</td><td>No data</td><td><code>inbox</code> (2xl)</td></tr>
|
|
646
|
+
<tr><td>Empty state</td><td>No results</td><td><code>search-x</code> (2xl)</td></tr>
|
|
647
|
+
</tbody>
|
|
648
|
+
</table>
|
|
649
|
+
</div>
|
|
650
|
+
</div>
|
|
651
|
+
<div class="card">
|
|
652
|
+
<div class="card-title">Toggle pairs</div>
|
|
653
|
+
<p style="font-size:12px;color:var(--inkblot-semantic-color-text-secondary);margin-bottom:var(--inkblot-spacing-3)">Binary state icons. Swap on toggle. Update <code>aria-label</code> to reflect the new action.</p>
|
|
654
|
+
<div class="table-scroll">
|
|
655
|
+
<table class="icon-table">
|
|
656
|
+
<thead><tr><th>State A</th><th>State B</th><th>Use case</th></tr></thead>
|
|
657
|
+
<tbody>
|
|
658
|
+
<tr><td><code>eye</code></td><td><code>eye-off</code></td><td>Password visibility</td></tr>
|
|
659
|
+
<tr><td><code>bell</code></td><td><code>bell-off</code></td><td>Notifications</td></tr>
|
|
660
|
+
<tr><td><code>volume-2</code></td><td><code>volume-x</code></td><td>Sound</td></tr>
|
|
661
|
+
<tr><td><code>lock</code></td><td><code>unlock</code></td><td>Lock state</td></tr>
|
|
662
|
+
<tr><td><code>moon</code></td><td><code>sun</code></td><td>Dark mode</td></tr>
|
|
663
|
+
<tr><td><code>star</code> (filled)</td><td><code>star</code> (stroke)</td><td>Favorite</td></tr>
|
|
664
|
+
<tr><td><code>heart</code> (filled)</td><td><code>heart</code> (stroke)</td><td>Like</td></tr>
|
|
665
|
+
<tr><td><code>layout-grid</code></td><td><code>list</code></td><td>View mode</td></tr>
|
|
666
|
+
</tbody>
|
|
667
|
+
</table>
|
|
668
|
+
</div>
|
|
669
|
+
</div>
|
|
670
|
+
</div>
|
|
671
|
+
</section>
|
|
672
|
+
|
|
673
|
+
<!-- RESPONSIVE -->
|
|
674
|
+
<section class="section" id="responsive">
|
|
675
|
+
<div class="section-header"><h2 class="section-title">Responsive behavior</h2><p class="section-desc">How components adapt across breakpoints</p></div>
|
|
676
|
+
<div class="three-col">
|
|
677
|
+
<div class="card">
|
|
678
|
+
<div class="card-title">Card grid</div>
|
|
679
|
+
<div class="responsive-vis"><div style="background:var(--inkblot-color-accent-citron-600);grid-column:span 4">1 col mobile</div></div>
|
|
680
|
+
<div class="responsive-vis" style="margin-top:var(--inkblot-spacing-2)"><div style="background:var(--inkblot-color-accent-citron-500);grid-column:span 2;color:var(--inkblot-color-accent-citron-50)">2 col</div><div style="background:var(--inkblot-color-accent-citron-700);grid-column:span 2">tablet</div></div>
|
|
681
|
+
<div class="responsive-vis" style="margin-top:var(--inkblot-spacing-2)"><div style="background:var(--inkblot-color-accent-citron-300);color:var(--inkblot-color-accent-citron-800)">3</div><div style="background:var(--inkblot-color-accent-citron-500);color:var(--inkblot-color-accent-citron-50)">col</div><div style="background:var(--inkblot-color-accent-citron-700)">desk</div><div style="background:var(--inkblot-color-accent-citron-900)">top</div></div>
|
|
682
|
+
</div>
|
|
683
|
+
<div class="card">
|
|
684
|
+
<div class="card-title">Sidebar</div>
|
|
685
|
+
<div style="display:flex;gap:2px;height:120px;border-radius:var(--inkblot-radius-lg);overflow:hidden">
|
|
686
|
+
<div style="width:64px;background:var(--inkblot-color-accent-citron-700);display:flex;align-items:center;justify-content:center;color:white;font-size:10px;writing-mode:vertical-lr">sidebar</div>
|
|
687
|
+
<div style="flex:1;background:var(--inkblot-color-accent-citron-200);display:flex;align-items:center;justify-content:center;font-size:10px;color:var(--inkblot-color-accent-citron-800)">main content</div>
|
|
688
|
+
</div>
|
|
689
|
+
<div style="font-size:10px;color:var(--inkblot-semantic-color-text-tertiary);margin-top:var(--inkblot-spacing-2)">Mobile: drawer · Tablet: 64px rail · Desktop: 280px</div>
|
|
690
|
+
</div>
|
|
691
|
+
<div class="card">
|
|
692
|
+
<div class="card-title">Modal</div>
|
|
693
|
+
<div style="display:flex;gap:var(--inkblot-spacing-3);align-items:flex-end">
|
|
694
|
+
<div style="width:48px;height:80px;background:var(--inkblot-color-accent-citron-600);border-radius:var(--inkblot-radius-md);display:flex;align-items:flex-end;justify-content:center;color:white;font-size:9px;padding-bottom:4px">full</div>
|
|
695
|
+
<div style="width:64px;height:56px;background:var(--inkblot-color-accent-citron-500);border-radius:var(--inkblot-radius-lg);display:flex;align-items:center;justify-content:center;color:white;font-size:9px">centered</div>
|
|
696
|
+
<div style="width:80px;height:56px;background:var(--inkblot-color-accent-citron-400);border-radius:var(--inkblot-radius-lg);display:flex;align-items:center;justify-content:center;color:white;font-size:9px">desktop</div>
|
|
697
|
+
</div>
|
|
698
|
+
<div style="font-size:10px;color:var(--inkblot-semantic-color-text-tertiary);margin-top:var(--inkblot-spacing-2)">Mobile: full-screen · Tablet: centered · Desktop: sized</div>
|
|
699
|
+
</div>
|
|
700
|
+
</div>
|
|
701
|
+
</section>
|
|
702
|
+
|
|
703
|
+
<!-- STATE COMBINATIONS -->
|
|
704
|
+
<section class="section" id="states-multi">
|
|
705
|
+
<div class="section-header"><h2 class="section-title">State combinations</h2><p class="section-desc">Priority: disabled > loading > error > active > focus > hover > default</p></div>
|
|
706
|
+
<div class="two-col">
|
|
707
|
+
<div class="card">
|
|
708
|
+
<div class="card-title">Priority table</div>
|
|
709
|
+
<div class="table-scroll">
|
|
710
|
+
<table class="state-table">
|
|
711
|
+
<thead><tr><th>Combination</th><th>Result</th></tr></thead>
|
|
712
|
+
<tbody>
|
|
713
|
+
<tr><td><code>disabled</code> + <code>hover</code></td><td>Disabled wins. No hover. <code>cursor: not-allowed</code></td></tr>
|
|
714
|
+
<tr><td><code>disabled</code> + <code>focus</code></td><td>Disabled visual, but focus ring stays (a11y)</td></tr>
|
|
715
|
+
<tr><td><code>error</code> + <code>focus</code></td><td>Both: error border + focus ring via box-shadow</td></tr>
|
|
716
|
+
<tr><td><code>loading</code> + <code>hover</code></td><td>Loading wins. <code>cursor: wait</code></td></tr>
|
|
717
|
+
<tr><td><code>error</code> + <code>hover</code></td><td>Error border stays. Subtle hover bg allowed</td></tr>
|
|
718
|
+
</tbody>
|
|
719
|
+
</table>
|
|
720
|
+
</div>
|
|
721
|
+
</div>
|
|
722
|
+
<div class="card">
|
|
723
|
+
<div class="card-title">Live examples</div>
|
|
724
|
+
<div style="display:flex;flex-direction:column;gap:var(--inkblot-spacing-3)">
|
|
725
|
+
<div class="input-group"><label class="input-label">Error + Focus</label><input class="input input-error" value="Click to see both states" style="box-shadow:none" onfocus="this.style.boxShadow='0 0 0 2px rgba(230,53,43,0.15)'" onblur="this.style.boxShadow='none'" /></div>
|
|
726
|
+
<div class="input-group"><label class="input-label">Disabled</label><input class="input" value="Cannot interact" disabled style="opacity:var(--inkblot-opacity-disabled)" /></div>
|
|
727
|
+
<div style="display:flex;gap:var(--inkblot-spacing-3)">
|
|
728
|
+
<button class="btn btn-primary" disabled style="opacity:var(--inkblot-opacity-disabled);cursor:not-allowed">Disabled btn</button>
|
|
729
|
+
<button class="btn btn-primary" style="cursor:wait;position:relative"><span style="opacity:0.5">Loading...</span></button>
|
|
730
|
+
</div>
|
|
731
|
+
</div>
|
|
732
|
+
</div>
|
|
733
|
+
</div>
|
|
734
|
+
</section>
|
|
735
|
+
|
|
736
|
+
<!-- INTERACTION FLOWS -->
|
|
737
|
+
<section class="section" id="interactions">
|
|
738
|
+
<div class="section-header"><h2 class="section-title">Interaction flows</h2><p class="section-desc">Deterministic event chains for AI agents</p></div>
|
|
739
|
+
<div class="three-col">
|
|
740
|
+
<div class="card">
|
|
741
|
+
<div class="card-title">Form submission</div>
|
|
742
|
+
<div class="flow-steps">
|
|
743
|
+
<div class="flow-step"><span>Validate all required fields</span></div>
|
|
744
|
+
<div class="flow-step"><span>If errors: scroll to first, set focus, announce</span></div>
|
|
745
|
+
<div class="flow-step"><span>If valid: button → loading state</span></div>
|
|
746
|
+
<div class="flow-step"><span>Send data. Success → toast + redirect</span></div>
|
|
747
|
+
<div class="flow-step"><span>Failure → error toast, restore button</span></div>
|
|
748
|
+
</div>
|
|
749
|
+
</div>
|
|
750
|
+
<div class="card">
|
|
751
|
+
<div class="card-title">Modal lifecycle</div>
|
|
752
|
+
<div class="flow-steps">
|
|
753
|
+
<div class="flow-step"><span>Record trigger element</span></div>
|
|
754
|
+
<div class="flow-step"><span>Render + animate in (250ms)</span></div>
|
|
755
|
+
<div class="flow-step"><span>Lock scroll, trap focus</span></div>
|
|
756
|
+
<div class="flow-step"><span>Close: Escape / overlay / X</span></div>
|
|
757
|
+
<div class="flow-step"><span>Return focus to trigger</span></div>
|
|
758
|
+
</div>
|
|
759
|
+
</div>
|
|
760
|
+
<div class="card">
|
|
761
|
+
<div class="card-title">Dropdown</div>
|
|
762
|
+
<div class="flow-steps">
|
|
763
|
+
<div class="flow-step"><span>Click/Enter → aria-expanded="true"</span></div>
|
|
764
|
+
<div class="flow-step"><span>Animate in (slideDown 200ms)</span></div>
|
|
765
|
+
<div class="flow-step"><span>Arrow keys navigate options</span></div>
|
|
766
|
+
<div class="flow-step"><span>Enter selects, Escape closes</span></div>
|
|
767
|
+
<div class="flow-step"><span>Return focus to trigger</span></div>
|
|
768
|
+
</div>
|
|
769
|
+
</div>
|
|
770
|
+
</div>
|
|
771
|
+
</section>
|
|
772
|
+
|
|
773
|
+
<!-- CONTENT RULES -->
|
|
774
|
+
<section class="section" id="content">
|
|
775
|
+
<div class="section-header"><h2 class="section-title">Content rules</h2><p class="section-desc">Truncation · Aspect ratios · Empty values · Image fallbacks</p></div>
|
|
776
|
+
<div class="two-col">
|
|
777
|
+
<div class="card">
|
|
778
|
+
<div class="card-title">Truncation</div>
|
|
779
|
+
<div style="margin-bottom:var(--inkblot-spacing-4)">
|
|
780
|
+
<div style="font-size:10px;font-weight:500;color:var(--inkblot-semantic-color-text-tertiary);margin-bottom:4px">SINGLE LINE</div>
|
|
781
|
+
<div class="truncate-1" style="font-size:var(--inkblot-typography-font-size-sm);max-width:240px;background:var(--inkblot-semantic-color-background-tertiary);padding:var(--inkblot-spacing-2) var(--inkblot-spacing-3);border-radius:var(--inkblot-radius-md)">This is a very long piece of text that will be truncated with an ellipsis at the end</div>
|
|
782
|
+
</div>
|
|
783
|
+
<div style="margin-bottom:var(--inkblot-spacing-4)">
|
|
784
|
+
<div style="font-size:10px;font-weight:500;color:var(--inkblot-semantic-color-text-tertiary);margin-bottom:4px">MULTI LINE (3)</div>
|
|
785
|
+
<div class="truncate-3" style="font-size:var(--inkblot-typography-font-size-sm);max-width:280px;background:var(--inkblot-semantic-color-background-tertiary);padding:var(--inkblot-spacing-2) var(--inkblot-spacing-3);border-radius:var(--inkblot-radius-md)">This is a longer piece of text that spans multiple lines. It will be clamped to exactly three lines using -webkit-line-clamp. Any content beyond the third line is hidden with an ellipsis.</div>
|
|
786
|
+
</div>
|
|
787
|
+
<div>
|
|
788
|
+
<div style="font-size:10px;font-weight:500;color:var(--inkblot-semantic-color-text-tertiary);margin-bottom:4px">EMPTY VALUES</div>
|
|
789
|
+
<div style="font-size:var(--inkblot-typography-font-size-sm);display:flex;gap:var(--inkblot-spacing-4)">
|
|
790
|
+
<span>Name: <strong>—</strong></span>
|
|
791
|
+
<span>Date: <strong>—</strong></span>
|
|
792
|
+
<span>Count: <strong>—</strong></span>
|
|
793
|
+
</div>
|
|
794
|
+
</div>
|
|
795
|
+
</div>
|
|
796
|
+
<div class="card">
|
|
797
|
+
<div class="card-title">Aspect ratios</div>
|
|
798
|
+
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:var(--inkblot-spacing-3)">
|
|
799
|
+
<div><div class="aspect-box" style="aspect-ratio:16/9">16:9</div><div style="font-size:10px;color:var(--inkblot-semantic-color-text-tertiary);margin-top:4px">landscape</div></div>
|
|
800
|
+
<div><div class="aspect-box" style="aspect-ratio:1/1">1:1</div><div style="font-size:10px;color:var(--inkblot-semantic-color-text-tertiary);margin-top:4px">square</div></div>
|
|
801
|
+
<div><div class="aspect-box" style="aspect-ratio:3/4">3:4</div><div style="font-size:10px;color:var(--inkblot-semantic-color-text-tertiary);margin-top:4px">portrait</div></div>
|
|
802
|
+
</div>
|
|
803
|
+
<div style="margin-top:var(--inkblot-spacing-4)">
|
|
804
|
+
<div style="font-size:10px;font-weight:500;color:var(--inkblot-semantic-color-text-tertiary);margin-bottom:4px">IMAGE FALLBACK</div>
|
|
805
|
+
<div style="background:var(--inkblot-semantic-color-background-tertiary);border-radius:var(--inkblot-radius-lg);aspect-ratio:16/9;display:flex;align-items:center;justify-content:center;color:var(--inkblot-semantic-color-text-tertiary);font-size:var(--inkblot-typography-font-size-sm)">Image failed to load</div>
|
|
806
|
+
</div>
|
|
807
|
+
</div>
|
|
808
|
+
</div>
|
|
809
|
+
</section>
|
|
810
|
+
|
|
811
|
+
<!-- PERFORMANCE -->
|
|
812
|
+
<section class="section" id="performance">
|
|
813
|
+
<div class="section-header"><h2 class="section-title">Performance hints</h2><p class="section-desc">Optimization flags for AI-generated code</p></div>
|
|
814
|
+
<div class="perf-grid">
|
|
815
|
+
<div class="perf-item"><h4>Lazy loading</h4><p>Images below fold: <code style="font-size:10px;background:var(--inkblot-semantic-color-background-tertiary);padding:1px 4px;border-radius:3px">loading="lazy"</code>. Components: React.lazy + Suspense.</p></div>
|
|
816
|
+
<div class="perf-item"><h4>Virtualization</h4><p>Lists > 50 items: react-window. Tables > 100 rows: virtualize. > 500: paginate.</p></div>
|
|
817
|
+
<div class="perf-item"><h4>CSS animations</h4><p>Prefer transform + opacity. Never animate width/height/margin. Use will-change sparingly.</p></div>
|
|
818
|
+
<div class="perf-item"><h4>Rendering</h4><p>SSR for content/SEO. CSR for dashboards. React.memo on list items and table rows.</p></div>
|
|
819
|
+
<div class="perf-item"><h4>Assets</h4><p>WebP/AVIF images. System font stack. Inline SVG for interactive icons.</p></div>
|
|
820
|
+
<div class="perf-item"><h4>Code splitting</h4><p>Route-level splits. Dynamic import() for modals, heavy charts, settings panels.</p></div>
|
|
821
|
+
</div>
|
|
822
|
+
</section>
|
|
823
|
+
|
|
824
|
+
<!-- SPACING & RADIUS -->
|
|
825
|
+
<section class="section" id="spacing">
|
|
826
|
+
<div class="section-header"><h2 class="section-title">Spacing & Radius</h2><p class="section-desc">4pt grid · Consistent rhythm · Corner treatment</p></div>
|
|
827
|
+
<div class="two-col">
|
|
828
|
+
<div>
|
|
829
|
+
<div class="card-title">Spacing scale</div>
|
|
830
|
+
<div class="scale-row">
|
|
831
|
+
<div class="scale-box" style="width:var(--inkblot-spacing-1);height:var(--inkblot-spacing-1)"></div>
|
|
832
|
+
<div class="scale-box" style="width:var(--inkblot-spacing-2);height:var(--inkblot-spacing-2)">2</div>
|
|
833
|
+
<div class="scale-box" style="width:var(--inkblot-spacing-3);height:var(--inkblot-spacing-3)">3</div>
|
|
834
|
+
<div class="scale-box" style="width:var(--inkblot-spacing-4);height:var(--inkblot-spacing-4)">4</div>
|
|
835
|
+
<div class="scale-box" style="width:var(--inkblot-spacing-6);height:var(--inkblot-spacing-6)">6</div>
|
|
836
|
+
<div class="scale-box" style="width:var(--inkblot-spacing-8);height:var(--inkblot-spacing-8)">8</div>
|
|
837
|
+
<div class="scale-box" style="width:var(--inkblot-spacing-12);height:var(--inkblot-spacing-12)">12</div>
|
|
838
|
+
</div>
|
|
839
|
+
</div>
|
|
840
|
+
<div>
|
|
841
|
+
<div class="card-title">Border radius</div>
|
|
842
|
+
<div class="radius-row">
|
|
843
|
+
<div class="radius-box" style="border-radius:var(--inkblot-radius-none)">none</div>
|
|
844
|
+
<div class="radius-box" style="border-radius:var(--inkblot-radius-sm)">sm</div>
|
|
845
|
+
<div class="radius-box" style="border-radius:var(--inkblot-radius-md)">md</div>
|
|
846
|
+
<div class="radius-box" style="border-radius:var(--inkblot-radius-lg)">lg</div>
|
|
847
|
+
<div class="radius-box" style="border-radius:var(--inkblot-radius-xl)">xl</div>
|
|
848
|
+
<div class="radius-box" style="border-radius:var(--inkblot-radius-full)">full</div>
|
|
849
|
+
</div>
|
|
850
|
+
</div>
|
|
851
|
+
</div>
|
|
852
|
+
</section>
|
|
853
|
+
|
|
854
|
+
<!-- ELEVATION -->
|
|
855
|
+
<section class="section" id="elevation">
|
|
856
|
+
<div class="section-header"><h2 class="section-title">Elevation</h2><p class="section-desc">Shadow depth · Visual hierarchy</p></div>
|
|
857
|
+
<div class="shadow-row">
|
|
858
|
+
<div class="shadow-box" style="box-shadow:none">none</div>
|
|
859
|
+
<div class="shadow-box" style="box-shadow:var(--inkblot-shadow-xs)">xs</div>
|
|
860
|
+
<div class="shadow-box" style="box-shadow:var(--inkblot-shadow-sm)">sm</div>
|
|
861
|
+
<div class="shadow-box" style="box-shadow:var(--inkblot-shadow-md)">md</div>
|
|
862
|
+
<div class="shadow-box" style="box-shadow:var(--inkblot-shadow-lg)">lg</div>
|
|
863
|
+
<div class="shadow-box" style="box-shadow:var(--inkblot-shadow-xl)">xl</div>
|
|
864
|
+
</div>
|
|
865
|
+
</section>
|
|
866
|
+
|
|
867
|
+
<!-- CONTENT STATES -->
|
|
868
|
+
<section class="section" id="states">
|
|
869
|
+
<div class="section-header"><h2 class="section-title">Content states</h2><p class="section-desc">Loading · Empty · Error</p></div>
|
|
870
|
+
<div class="three-col">
|
|
871
|
+
<div class="card">
|
|
872
|
+
<div class="card-title">Loading skeleton</div>
|
|
873
|
+
<div style="display:flex;flex-direction:column;gap:var(--inkblot-spacing-3)">
|
|
874
|
+
<div class="skeleton" style="width:40%;height:24px"></div>
|
|
875
|
+
<div class="skeleton" style="width:100%;height:14px"></div>
|
|
876
|
+
<div class="skeleton" style="width:80%;height:14px"></div>
|
|
877
|
+
<div class="skeleton" style="width:30%;height:32px;margin-top:var(--inkblot-spacing-2)"></div>
|
|
878
|
+
</div>
|
|
879
|
+
</div>
|
|
880
|
+
<div class="card">
|
|
881
|
+
<div class="card-title">Empty state</div>
|
|
882
|
+
<div class="empty-state"><h3>No results</h3><p>Try adjusting your search or filters.</p><button class="btn btn-primary btn-sm">Clear filters</button></div>
|
|
883
|
+
</div>
|
|
884
|
+
<div class="card">
|
|
885
|
+
<div class="card-title">Error banner</div>
|
|
886
|
+
<div class="error-demo">
|
|
887
|
+
<div style="font-weight:600;color:var(--inkblot-semantic-color-status-error);margin-bottom:4px">Connection failed</div>
|
|
888
|
+
<div style="color:var(--inkblot-semantic-color-text-secondary)">Unable to reach the server. Please check your connection and try again.</div>
|
|
889
|
+
</div>
|
|
890
|
+
</div>
|
|
891
|
+
</div>
|
|
892
|
+
</section>
|
|
893
|
+
|
|
894
|
+
</main>
|
|
895
|
+
|
|
896
|
+
<!-- MODAL -->
|
|
897
|
+
<div class="modal-overlay" id="modal-overlay">
|
|
898
|
+
<div class="modal-dialog" role="dialog" aria-modal="true" aria-labelledby="modal-title">
|
|
899
|
+
<h3 id="modal-title">Confirm action</h3>
|
|
900
|
+
<p>This modal demonstrates focus trapping, overlay click to close, and Escape key dismissal.</p>
|
|
901
|
+
<div style="display:flex;gap:var(--inkblot-spacing-3);justify-content:flex-end">
|
|
902
|
+
<button class="btn btn-secondary" id="modal-cancel">Cancel</button>
|
|
903
|
+
<button class="btn btn-primary" id="modal-confirm">Confirm</button>
|
|
904
|
+
</div>
|
|
905
|
+
</div>
|
|
906
|
+
</div>
|
|
907
|
+
|
|
908
|
+
<footer class="footer">Citron Design System v1.0 · Tokens in <code>tokens/</code> · Specs in <code>system/</code></footer>
|
|
909
|
+
|
|
910
|
+
<script>
|
|
911
|
+
(function() {
|
|
912
|
+
var nav = document.querySelector('.nav');
|
|
913
|
+
var hamburger = document.getElementById('nav-hamburger');
|
|
914
|
+
var menu = document.getElementById('nav-menu');
|
|
915
|
+
if (!hamburger || !menu) return;
|
|
916
|
+
function openMenu() { nav.classList.add('open'); hamburger.setAttribute('aria-expanded', 'true'); hamburger.setAttribute('aria-label', 'Close menu'); document.body.style.overflow = 'hidden'; }
|
|
917
|
+
function closeMenu() { nav.classList.remove('open'); hamburger.setAttribute('aria-expanded', 'false'); hamburger.setAttribute('aria-label', 'Open menu'); document.body.style.overflow = ''; }
|
|
918
|
+
function toggleMenu() { nav.classList.contains('open') ? closeMenu() : openMenu(); }
|
|
919
|
+
hamburger.addEventListener('click', function(e) { e.stopPropagation(); toggleMenu(); });
|
|
920
|
+
menu.querySelectorAll('a').forEach(function(a) { a.addEventListener('click', closeMenu); });
|
|
921
|
+
document.addEventListener('click', function(e) { if (nav.classList.contains('open') && !nav.contains(e.target)) closeMenu(); });
|
|
922
|
+
document.addEventListener('keydown', function(e) { if (e.key === 'Escape' && nav.classList.contains('open')) closeMenu(); });
|
|
923
|
+
})();
|
|
924
|
+
|
|
925
|
+
document.getElementById('theme-toggle').addEventListener('click', function() {
|
|
926
|
+
var h = document.documentElement;
|
|
927
|
+
h.setAttribute('data-theme', h.getAttribute('data-theme') === 'dark' ? '' : 'dark');
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
document.querySelectorAll('.swatch[data-hex]').forEach(function(s) {
|
|
931
|
+
s.setAttribute('tabindex', '0');
|
|
932
|
+
s.addEventListener('click', function() {
|
|
933
|
+
var t = this.getAttribute('data-token');
|
|
934
|
+
navigator.clipboard.writeText('var(' + t + ')').then(function() {
|
|
935
|
+
var m = s.querySelector('.swatch-meta'), l = m.textContent;
|
|
936
|
+
m.textContent = 'Copied!'; s.classList.add('copied');
|
|
937
|
+
setTimeout(function() { m.textContent = l; s.classList.remove('copied'); }, 1200);
|
|
938
|
+
});
|
|
939
|
+
});
|
|
940
|
+
s.addEventListener('keydown', function(e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); this.click(); } });
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
var overlay = document.getElementById('modal-overlay');
|
|
944
|
+
document.getElementById('open-modal').addEventListener('click', function() {
|
|
945
|
+
overlay.classList.add('open');
|
|
946
|
+
overlay.querySelector('.btn-secondary').focus();
|
|
947
|
+
document.body.style.overflow = 'hidden';
|
|
948
|
+
});
|
|
949
|
+
function closeModal() { overlay.classList.remove('open'); document.body.style.overflow = ''; document.getElementById('open-modal').focus(); }
|
|
950
|
+
document.getElementById('modal-cancel').addEventListener('click', closeModal);
|
|
951
|
+
document.getElementById('modal-confirm').addEventListener('click', closeModal);
|
|
952
|
+
overlay.addEventListener('click', function(e) { if (e.target === overlay) closeModal(); });
|
|
953
|
+
document.addEventListener('keydown', function(e) { if (e.key === 'Escape' && overlay.classList.contains('open')) closeModal(); });
|
|
954
|
+
</script>
|
|
955
|
+
</body>
|
|
956
|
+
</html>
|