picasso-skill 1.5.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/picasso.md +14 -2
- package/checklists/pre-ship.md +83 -0
- package/commands/backlog.md +34 -0
- package/commands/variants.md +18 -0
- package/package.json +3 -1
- package/references/accessibility-wcag.md +245 -0
- package/references/anti-patterns.md +184 -0
- package/references/color-and-contrast.md +477 -0
- package/references/component-patterns.md +113 -0
- package/references/conversion-design.md +193 -0
- package/references/data-visualization.md +226 -0
- package/references/depth-and-elevation.md +211 -0
- package/references/design-system.md +176 -0
- package/references/generative-art.md +648 -0
- package/references/interaction-design.md +162 -0
- package/references/modern-css-performance.md +361 -0
- package/references/motion-and-animation.md +267 -0
- package/references/performance-optimization.md +746 -0
- package/references/react-patterns.md +318 -0
- package/references/responsive-design.md +452 -0
- package/references/sensory-design.md +369 -0
- package/references/spatial-design.md +176 -0
- package/references/style-presets.md +502 -0
- package/references/tools-catalog.md +103 -0
- package/references/typography.md +415 -0
- package/references/ux-psychology.md +235 -0
- package/references/ux-writing.md +513 -0
- package/skills/picasso/SKILL.md +58 -2
- package/skills/picasso/references/animation-performance.md +244 -0
- package/skills/picasso/references/brand-and-identity.md +136 -0
- package/skills/picasso/references/code-typography.md +222 -0
- package/skills/picasso/references/color-and-contrast.md +56 -2
- package/skills/picasso/references/dark-mode.md +199 -0
- package/skills/picasso/references/depth-and-elevation.md +211 -0
- package/skills/picasso/references/i18n-visual-patterns.md +177 -0
- package/skills/picasso/references/images-and-media.md +222 -0
- package/skills/picasso/references/loading-and-states.md +258 -0
- package/skills/picasso/references/micro-interactions.md +291 -0
- package/skills/picasso/references/motion-and-animation.md +9 -2
- package/skills/picasso/references/navigation-patterns.md +247 -0
- package/skills/picasso/references/style-presets.md +1 -1
- package/skills/picasso/references/tables-and-forms.md +227 -0
- package/skills/picasso/references/tools-catalog.md +103 -0
- package/skills/picasso/references/typography.md +45 -2
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Dark Mode Reference
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
1. Preference Hierarchy
|
|
5
|
+
2. CSS Custom Properties Approach
|
|
6
|
+
3. Mode Transition
|
|
7
|
+
4. Surface Elevation in Dark Mode
|
|
8
|
+
5. Color Adjustments
|
|
9
|
+
6. Image and Media Handling
|
|
10
|
+
7. Testing Dark Mode
|
|
11
|
+
8. Forced Colors and High Contrast
|
|
12
|
+
9. Common Mistakes
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 1. Preference Hierarchy
|
|
17
|
+
|
|
18
|
+
System preference is the default. User override persists via localStorage. Never force dark mode without a toggle.
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
// Check system preference, then user override
|
|
22
|
+
const stored = localStorage.getItem('theme');
|
|
23
|
+
const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
24
|
+
const isDark = stored ? stored === 'dark' : systemDark;
|
|
25
|
+
document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```css
|
|
29
|
+
/* System preference as base */
|
|
30
|
+
:root { color-scheme: light dark; }
|
|
31
|
+
|
|
32
|
+
/* Light (default) */
|
|
33
|
+
:root, [data-theme="light"] {
|
|
34
|
+
--surface-0: oklch(0.99 0.005 var(--hue));
|
|
35
|
+
--text-primary: oklch(0.15 0.02 var(--hue));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Dark */
|
|
39
|
+
[data-theme="dark"] {
|
|
40
|
+
--surface-0: oklch(0.11 0.02 var(--hue));
|
|
41
|
+
--text-primary: oklch(0.93 0.01 var(--hue));
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 2. CSS Custom Properties Approach
|
|
48
|
+
|
|
49
|
+
Define all colors as CSS variables. Never hardcode hex in components. Dark mode is a variable swap, not a rewrite.
|
|
50
|
+
|
|
51
|
+
```css
|
|
52
|
+
[data-theme="dark"] {
|
|
53
|
+
--surface-0: oklch(0.11 0.02 230); /* page bg */
|
|
54
|
+
--surface-1: oklch(0.14 0.02 230); /* card bg */
|
|
55
|
+
--surface-2: oklch(0.17 0.022 230); /* elevated bg */
|
|
56
|
+
--surface-3: oklch(0.20 0.024 230); /* hover bg */
|
|
57
|
+
--border: oklch(0.22 0.015 230);
|
|
58
|
+
--text-primary: oklch(0.93 0.01 230);
|
|
59
|
+
--text-secondary: oklch(0.62 0.02 230);
|
|
60
|
+
--text-muted: oklch(0.42 0.015 230);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 3. Mode Transition
|
|
67
|
+
|
|
68
|
+
Never instant-swap. Use a 200ms opacity transition on the body. Disable transitions during the swap to prevent every element animating individually.
|
|
69
|
+
|
|
70
|
+
```css
|
|
71
|
+
/* Add this class during theme switch, remove after 200ms */
|
|
72
|
+
.theme-transitioning * {
|
|
73
|
+
transition: none !important;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
body {
|
|
77
|
+
transition: background-color 0.2s ease-out, color 0.2s ease-out;
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
function toggleTheme() {
|
|
83
|
+
document.body.classList.add('theme-transitioning');
|
|
84
|
+
const next = document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark';
|
|
85
|
+
document.documentElement.dataset.theme = next;
|
|
86
|
+
localStorage.setItem('theme', next);
|
|
87
|
+
requestAnimationFrame(() => {
|
|
88
|
+
requestAnimationFrame(() => {
|
|
89
|
+
document.body.classList.remove('theme-transitioning');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 4. Surface Elevation in Dark Mode
|
|
98
|
+
|
|
99
|
+
In dark mode, elevated surfaces get LIGHTER, not darker. This is the opposite of light mode where shadows darken surfaces. Without this, dark mode looks flat.
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
Light mode elevation: surface-0 (lightest) → surface-3 (slightly darker)
|
|
103
|
+
Dark mode elevation: surface-0 (darkest) → surface-3 (slightly lighter)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Shadows are nearly invisible in dark mode. Replace with surface lightness differentiation and subtle border glow:
|
|
107
|
+
|
|
108
|
+
```css
|
|
109
|
+
[data-theme="dark"] .card {
|
|
110
|
+
background: var(--surface-1);
|
|
111
|
+
border: 1px solid oklch(1 0 0 / 0.06);
|
|
112
|
+
/* Shadow optional — use border + surface tint instead */
|
|
113
|
+
box-shadow: 0 0 0 1px oklch(1 0 0 / 0.03);
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 5. Color Adjustments
|
|
120
|
+
|
|
121
|
+
Accent colors need lower chroma in dark mode. Full-saturation accents on dark backgrounds are harsh.
|
|
122
|
+
|
|
123
|
+
```css
|
|
124
|
+
:root { --accent: oklch(0.55 0.25 250); } /* light: saturated */
|
|
125
|
+
[data-theme="dark"] { --accent: oklch(0.65 0.18 250); } /* dark: lighter, less chroma */
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Semantic colors also need adjustment:
|
|
129
|
+
- Success green: lighter, less saturated
|
|
130
|
+
- Error red: lighter to maintain contrast
|
|
131
|
+
- Warning amber: reduce chroma to avoid glowing
|
|
132
|
+
|
|
133
|
+
Text colors: minimum lightness 0.60 in OKLCH for body text on dark backgrounds. Below 0.55 looks washed out on cheap screens even if it passes WCAG.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 6. Image and Media Handling
|
|
138
|
+
|
|
139
|
+
Dim images slightly in dark mode to reduce glare. SVGs should use currentColor or CSS variables.
|
|
140
|
+
|
|
141
|
+
```css
|
|
142
|
+
[data-theme="dark"] img:not([data-no-dim]) {
|
|
143
|
+
filter: brightness(0.9) contrast(1.05);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
[data-theme="dark"] svg { color: var(--text-primary); }
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
For decorative images, consider `mix-blend-mode: luminosity` to desaturate:
|
|
150
|
+
```css
|
|
151
|
+
[data-theme="dark"] .hero-image {
|
|
152
|
+
mix-blend-mode: luminosity;
|
|
153
|
+
opacity: 0.8;
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 7. Testing Dark Mode
|
|
160
|
+
|
|
161
|
+
Dark mode is not "invert the colors." Test these specifically:
|
|
162
|
+
|
|
163
|
+
1. **Contrast ratios** — recheck all text/background pairs. WCAG ratios change.
|
|
164
|
+
2. **Shadows** — are they visible? If not, use border or surface tint instead.
|
|
165
|
+
3. **Form inputs** — autofill styling overrides your dark background. Fix with `-webkit-box-shadow: 0 0 0 1000px var(--surface-1) inset`.
|
|
166
|
+
4. **Selection color** — `::selection` background needs to contrast with dark text highlight.
|
|
167
|
+
5. **Scrollbar** — custom scrollbar thumb must be visible on dark track.
|
|
168
|
+
6. **Third-party embeds** — iframes, maps, payment forms may not respect your dark mode.
|
|
169
|
+
7. **Screenshots** — take a screenshot and look at it on a non-retina screen. Subtle contrast often vanishes.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## 8. Forced Colors and High Contrast
|
|
174
|
+
|
|
175
|
+
Windows High Contrast mode (`@media (forced-colors: active)`) overrides ALL custom colors. Your tinted neutrals, subtle borders, and custom focus rings will disappear.
|
|
176
|
+
|
|
177
|
+
```css
|
|
178
|
+
@media (forced-colors: active) {
|
|
179
|
+
.card { border: 1px solid CanvasText; }
|
|
180
|
+
:focus-visible { outline: 2px solid Highlight; }
|
|
181
|
+
.btn-primary { border: 2px solid ButtonText; }
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Never use `forced-color-adjust: none` globally. Only on specific elements where you've provided a system-color fallback.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## 9. Common Mistakes
|
|
190
|
+
|
|
191
|
+
- **Instant theme swap without transition.** Jarring. Always fade (200ms).
|
|
192
|
+
- **Shadows that work in light but vanish in dark.** Replace with borders + surface tint.
|
|
193
|
+
- **Same accent chroma in both modes.** Reduce chroma for dark mode.
|
|
194
|
+
- **Checking contrast only in light mode.** Dark mode ratios are different — recheck.
|
|
195
|
+
- **`background: black` for dark mode.** Never pure black. Tint toward your hue.
|
|
196
|
+
- **Not testing autofill.** Browsers apply white backgrounds to autofilled inputs.
|
|
197
|
+
- **Not testing scrollbar.** Custom scrollbar may be invisible on dark backgrounds.
|
|
198
|
+
- **Storing theme in state instead of localStorage.** Causes flash of wrong theme on reload.
|
|
199
|
+
- **No `color-scheme: dark` declaration.** Browser UI elements (scrollbars, form controls) won't adapt without it.
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Depth and Elevation Reference
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
1. The Layering Technique
|
|
5
|
+
2. Dual Shadow System
|
|
6
|
+
3. Inset Shadows
|
|
7
|
+
4. Gradient + Inner Shadow Combo
|
|
8
|
+
5. Semantic Z-Index Scale
|
|
9
|
+
6. Shadow Usage Guide
|
|
10
|
+
7. Dark Mode Shadow Adjustments
|
|
11
|
+
8. The "Remove Borders" Rule
|
|
12
|
+
9. Hover Elevation Pattern
|
|
13
|
+
10. Common Mistakes
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 1. The Layering Technique
|
|
18
|
+
|
|
19
|
+
The single highest-ROI technique for transforming flat UIs into ones that feel crafted. Three steps:
|
|
20
|
+
|
|
21
|
+
1. **Create color layers.** Generate 3-4 shades of your base color by incrementing lightness in OKLCH:
|
|
22
|
+
```css
|
|
23
|
+
--bg-deep: oklch(0.18 0.01 var(--hue)); /* page background (deepest) */
|
|
24
|
+
--bg-base: oklch(0.22 0.01 var(--hue)); /* card/section background */
|
|
25
|
+
--bg-raised: oklch(0.26 0.012 var(--hue)); /* elevated interactive elements */
|
|
26
|
+
--bg-highest: oklch(0.30 0.015 var(--hue)); /* selected/active states */
|
|
27
|
+
```
|
|
28
|
+
In light mode, reverse the direction: the page is lightest, and elevated elements are slightly darker or lighter depending on the effect.
|
|
29
|
+
|
|
30
|
+
2. **Layer elements.** Darker backgrounds feel recessed. Lighter surfaces feel elevated. Stack your components accordingly: page → section → card → interactive element.
|
|
31
|
+
|
|
32
|
+
3. **Add dual shadows.** A light inset shadow on top combined with a dark shadow at the bottom creates realistic depth. See section 2 below.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 2. Dual Shadow System
|
|
37
|
+
|
|
38
|
+
Combine a light inset highlight on the top edge with a dark drop shadow at the bottom. This simulates overhead light hitting a raised surface.
|
|
39
|
+
|
|
40
|
+
```css
|
|
41
|
+
/* Small: subtle, most use cases (cards at rest, navbar, buttons) */
|
|
42
|
+
--shadow-sm:
|
|
43
|
+
inset 0 1px 0 0 oklch(1 0 0 / 0.05),
|
|
44
|
+
0 1px 2px 0 oklch(0 0 0 / 0.15);
|
|
45
|
+
|
|
46
|
+
/* Medium: emphasis and hover states */
|
|
47
|
+
--shadow-md:
|
|
48
|
+
inset 0 1px 0 0 oklch(1 0 0 / 0.08),
|
|
49
|
+
0 2px 8px -2px oklch(0 0 0 / 0.2);
|
|
50
|
+
|
|
51
|
+
/* Large: modals, popovers, focused cards */
|
|
52
|
+
--shadow-lg:
|
|
53
|
+
inset 0 1px 0 0 oklch(1 0 0 / 0.1),
|
|
54
|
+
0 8px 24px -4px oklch(0 0 0 / 0.25);
|
|
55
|
+
|
|
56
|
+
/* Extra large: hero overlays, dramatic emphasis */
|
|
57
|
+
--shadow-xl:
|
|
58
|
+
0 16px 48px -8px oklch(0 0 0 / 0.3),
|
|
59
|
+
0 4px 12px -2px oklch(0 0 0 / 0.15);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
These values use OKLCH alpha for consistency with your color tokens. Adapt the hue to match your palette if desired (e.g., `oklch(0 0 0 / 0.15)` could become `oklch(0 0.01 var(--hue) / 0.15)` for tinted shadows).
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 3. Inset Shadows
|
|
67
|
+
|
|
68
|
+
For elements that should feel *recessed* into the surface (input fields, progress bars, table cells, code blocks):
|
|
69
|
+
|
|
70
|
+
```css
|
|
71
|
+
.recessed {
|
|
72
|
+
box-shadow:
|
|
73
|
+
inset 0 2px 4px 0 oklch(0 0 0 / 0.15), /* dark shadow at top */
|
|
74
|
+
inset 0 -1px 0 0 oklch(1 0 0 / 0.05); /* subtle light at bottom */
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Inset shadows reverse the mental model: instead of floating above the surface, the element sits below it. Use this for:
|
|
79
|
+
- Text inputs (the field feels carved into the form)
|
|
80
|
+
- Progress bar tracks (the bar sits inside a groove)
|
|
81
|
+
- Recessed stat cards (metrics feel embedded, not floating)
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## 4. Gradient + Inner Shadow Combo
|
|
86
|
+
|
|
87
|
+
Simulates directional light hitting a raised surface from above. Powerful for selected cards, active states, and CTAs:
|
|
88
|
+
|
|
89
|
+
```css
|
|
90
|
+
.elevated-card {
|
|
91
|
+
background: linear-gradient(
|
|
92
|
+
to bottom,
|
|
93
|
+
oklch(0.28 0.015 var(--hue)), /* lighter at top (light source) */
|
|
94
|
+
oklch(0.24 0.01 var(--hue)) /* darker at bottom */
|
|
95
|
+
);
|
|
96
|
+
box-shadow:
|
|
97
|
+
inset 0 1px 0 0 oklch(1 0 0 / 0.08), /* highlight at top edge */
|
|
98
|
+
0 2px 8px -2px oklch(0 0 0 / 0.2); /* shadow beneath */
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
This combination makes elements feel physically present. Reserve it for the most important interactive elements on the page -- overuse dilutes the effect.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 5. Semantic Z-Index Scale
|
|
107
|
+
|
|
108
|
+
Use named z-index values instead of magic numbers. This prevents z-index wars and makes the stacking context explicit:
|
|
109
|
+
|
|
110
|
+
```css
|
|
111
|
+
--z-dropdown: 100;
|
|
112
|
+
--z-sticky: 200;
|
|
113
|
+
--z-fixed: 300;
|
|
114
|
+
--z-modal: 400;
|
|
115
|
+
--z-toast: 500;
|
|
116
|
+
--z-tooltip: 600;
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Rules:
|
|
120
|
+
- Never use bare numbers. Always reference the variable.
|
|
121
|
+
- Never go above 600 unless you have a documented reason.
|
|
122
|
+
- Modals need a backdrop at `--z-modal - 1` (399).
|
|
123
|
+
- Toasts should float above modals so errors are visible during dialogs.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 6. Shadow Usage Guide
|
|
128
|
+
|
|
129
|
+
| Element | Shadow Level | Additional Treatment |
|
|
130
|
+
|---------|-------------|---------------------|
|
|
131
|
+
| Cards (default) | sm | -- |
|
|
132
|
+
| Cards (hover) | md | transition 150ms |
|
|
133
|
+
| Selected card | md + gradient | light inset top |
|
|
134
|
+
| Modal / dialog | xl | overlay backdrop |
|
|
135
|
+
| Dropdown menu | lg | -- |
|
|
136
|
+
| Navbar (sticky) | sm | only when scrolled |
|
|
137
|
+
| Input (focus) | -- | accent ring instead |
|
|
138
|
+
| Recessed element | inset | dark top + light bottom |
|
|
139
|
+
| Progress bar | inset sm | inside container |
|
|
140
|
+
| Buttons (default) | sm | -- |
|
|
141
|
+
| Buttons (active) | inset sm | press-in effect |
|
|
142
|
+
| Tooltip | md | -- |
|
|
143
|
+
|
|
144
|
+
Not every element needs a shadow. Use shadows to communicate interactive hierarchy: elements the user can act on float above static content.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 7. Dark Mode Shadow Adjustments
|
|
149
|
+
|
|
150
|
+
Shadows are far less visible against dark backgrounds. Compensate with these techniques:
|
|
151
|
+
|
|
152
|
+
1. **Increase shadow opacity** by 0.05-0.10 compared to light mode values
|
|
153
|
+
2. **Add a subtle border** (1px `oklch(1 0 0 / 0.05)`) for edge definition where shadows alone are too faint
|
|
154
|
+
3. **Use inner glow** instead of drop shadows for the "elevated" feeling on small elements
|
|
155
|
+
4. **Higher surface = lighter background** -- in dark mode this is the primary depth cue, more important than shadows
|
|
156
|
+
|
|
157
|
+
```css
|
|
158
|
+
[data-theme="dark"] {
|
|
159
|
+
--shadow-sm:
|
|
160
|
+
inset 0 1px 0 0 oklch(1 0 0 / 0.03),
|
|
161
|
+
0 1px 3px 0 oklch(0 0 0 / 0.25);
|
|
162
|
+
--shadow-md:
|
|
163
|
+
inset 0 1px 0 0 oklch(1 0 0 / 0.05),
|
|
164
|
+
0 3px 12px -3px oklch(0 0 0 / 0.35);
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 8. The "Remove Borders" Rule
|
|
171
|
+
|
|
172
|
+
When you have established a clear color layering system (darker background → lighter card → lightest interactive element), borders on the lighter elements become redundant. The color contrast already creates visual separation. Remove them.
|
|
173
|
+
|
|
174
|
+
Keep borders only where:
|
|
175
|
+
- Color layering alone does not provide enough contrast (e.g., two surfaces with very similar lightness)
|
|
176
|
+
- You need to indicate a semantic boundary (form fields, table cells)
|
|
177
|
+
- The element is interactive and the border communicates state (focus, error)
|
|
178
|
+
|
|
179
|
+
This rule significantly reduces visual noise and makes the layering technique more effective.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 9. Hover Elevation Pattern
|
|
184
|
+
|
|
185
|
+
```css
|
|
186
|
+
.card {
|
|
187
|
+
box-shadow: var(--shadow-sm);
|
|
188
|
+
transition: box-shadow 150ms ease-out, transform 150ms ease-out;
|
|
189
|
+
}
|
|
190
|
+
.card:hover {
|
|
191
|
+
box-shadow: var(--shadow-md);
|
|
192
|
+
transform: translateY(-2px);
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Key constraints:
|
|
197
|
+
- Keep the translateY subtle: 1-3px maximum. Exaggerated lifts (5px+) feel cheap and dated.
|
|
198
|
+
- Always transition both shadow and transform together for a cohesive effect.
|
|
199
|
+
- On touch devices, this hover state should not apply (use `@media (hover: hover)`).
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 10. Common Mistakes
|
|
204
|
+
|
|
205
|
+
- Using the same `box-shadow` on every element (no hierarchy, everything looks the same)
|
|
206
|
+
- Shadow blur radius too large (20px+ on cards creates a fuzzy, unfocused look)
|
|
207
|
+
- Forgetting to adjust shadows for dark mode (invisible shadows destroy the depth system)
|
|
208
|
+
- Overusing shadows on flat, decorative elements that do not need elevation
|
|
209
|
+
- Using `filter: drop-shadow()` when `box-shadow` would suffice (drop-shadow does not support inset and renders differently)
|
|
210
|
+
- Relying solely on shadows for depth without the color layering foundation (shadows alone look stuck-on)
|
|
211
|
+
- Hard-coded z-index values like `z-index: 9999` (always use the semantic scale)
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Internationalization Visual Patterns Reference
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
1. Logical Properties
|
|
5
|
+
2. RTL Layout Mirroring
|
|
6
|
+
3. Text Expansion by Language
|
|
7
|
+
4. CJK Text Rendering
|
|
8
|
+
5. Number and Currency Formatting
|
|
9
|
+
6. Font Stacks for Multi-Language
|
|
10
|
+
7. Icon Mirroring in RTL
|
|
11
|
+
8. Common Mistakes
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. Logical Properties
|
|
16
|
+
|
|
17
|
+
Replace physical properties with logical ones. This makes RTL support automatic.
|
|
18
|
+
|
|
19
|
+
| Physical (avoid) | Logical (use) |
|
|
20
|
+
|---|---|
|
|
21
|
+
| `margin-left` | `margin-inline-start` |
|
|
22
|
+
| `margin-right` | `margin-inline-end` |
|
|
23
|
+
| `padding-left` | `padding-inline-start` |
|
|
24
|
+
| `text-align: left` | `text-align: start` |
|
|
25
|
+
| `float: left` | `float: inline-start` |
|
|
26
|
+
| `border-left` | `border-inline-start` |
|
|
27
|
+
| `left: 0` | `inset-inline-start: 0` |
|
|
28
|
+
|
|
29
|
+
```css
|
|
30
|
+
/* Good: works in both LTR and RTL */
|
|
31
|
+
.sidebar { margin-inline-end: 2rem; padding-inline-start: 1rem; }
|
|
32
|
+
|
|
33
|
+
/* Bad: breaks in RTL */
|
|
34
|
+
.sidebar { margin-right: 2rem; padding-left: 1rem; }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 2. RTL Layout Mirroring
|
|
40
|
+
|
|
41
|
+
Set `dir="auto"` on user-generated content. Set `dir="rtl"` on the `<html>` element for RTL languages.
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<html lang="ar" dir="rtl">
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Flexbox and Grid automatically reverse in RTL when using logical properties. No extra CSS needed.
|
|
48
|
+
|
|
49
|
+
```css
|
|
50
|
+
/* This works in both directions automatically */
|
|
51
|
+
.nav { display: flex; gap: 1rem; }
|
|
52
|
+
.card { display: grid; grid-template-columns: auto 1fr; }
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 3. Text Expansion by Language
|
|
58
|
+
|
|
59
|
+
English text expands significantly when translated. Design for the longest likely translation.
|
|
60
|
+
|
|
61
|
+
| Language | Expansion from English |
|
|
62
|
+
|---|---|
|
|
63
|
+
| German | +30-35% |
|
|
64
|
+
| French | +15-20% |
|
|
65
|
+
| Finnish | +30-40% |
|
|
66
|
+
| Russian | +15-25% |
|
|
67
|
+
| Chinese | -30-50% (shorter) |
|
|
68
|
+
| Japanese | -20-40% (shorter) |
|
|
69
|
+
| Arabic | +20-25% |
|
|
70
|
+
|
|
71
|
+
Rules:
|
|
72
|
+
- Never use fixed-width containers for translatable text.
|
|
73
|
+
- Buttons: use `min-width` not `width`. Allow text to wrap or grow.
|
|
74
|
+
- Navigation: test with German translations (longest common language).
|
|
75
|
+
- Truncate with `text-overflow: ellipsis` as a last resort, never as the design.
|
|
76
|
+
|
|
77
|
+
```css
|
|
78
|
+
/* Good: grows with content */
|
|
79
|
+
.btn { min-width: 120px; padding-inline: 1.5rem; white-space: nowrap; }
|
|
80
|
+
|
|
81
|
+
/* Bad: text overflows in German */
|
|
82
|
+
.btn { width: 120px; }
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 4. CJK Text Rendering
|
|
88
|
+
|
|
89
|
+
Chinese, Japanese, and Korean text has different line-breaking and spacing rules.
|
|
90
|
+
|
|
91
|
+
```css
|
|
92
|
+
/* Allow CJK text to break at any character */
|
|
93
|
+
.cjk-text {
|
|
94
|
+
line-break: auto;
|
|
95
|
+
word-break: keep-all; /* Korean: don't break within words */
|
|
96
|
+
overflow-wrap: break-word;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* CJK doesn't need letter-spacing for readability */
|
|
100
|
+
:lang(zh), :lang(ja), :lang(ko) {
|
|
101
|
+
letter-spacing: 0;
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
CJK text is denser — reduce line-height slightly:
|
|
106
|
+
```css
|
|
107
|
+
:lang(zh), :lang(ja) { line-height: 1.7; } /* vs 1.5 for Latin */
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 5. Number and Currency Formatting
|
|
113
|
+
|
|
114
|
+
Never hardcode currency symbols or number formats. Use `Intl.NumberFormat`.
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
// Automatic locale-aware formatting
|
|
118
|
+
new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(1234.56)
|
|
119
|
+
// → "$1,234.56"
|
|
120
|
+
|
|
121
|
+
new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(1234.56)
|
|
122
|
+
// → "1.234,56 €"
|
|
123
|
+
|
|
124
|
+
new Intl.NumberFormat('ja-JP', { style: 'currency', currency: 'JPY' }).format(1234)
|
|
125
|
+
// → "¥1,234"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
For dates: use `Intl.DateTimeFormat`, never manual formatting.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 6. Font Stacks for Multi-Language
|
|
133
|
+
|
|
134
|
+
Include system fonts per script as fallbacks:
|
|
135
|
+
|
|
136
|
+
```css
|
|
137
|
+
body {
|
|
138
|
+
font-family:
|
|
139
|
+
'Your Custom Font', /* Latin */
|
|
140
|
+
'Noto Sans SC', /* Simplified Chinese */
|
|
141
|
+
'Noto Sans JP', /* Japanese */
|
|
142
|
+
'Noto Sans KR', /* Korean */
|
|
143
|
+
'Noto Sans Arabic', /* Arabic */
|
|
144
|
+
system-ui, sans-serif; /* Fallback */
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Google's Noto family covers every Unicode script. Use it as the universal fallback.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 7. Icon Mirroring in RTL
|
|
153
|
+
|
|
154
|
+
Icons that imply direction MUST mirror in RTL. Icons that don't imply direction must NOT.
|
|
155
|
+
|
|
156
|
+
**Mirror in RTL:** arrows, back/forward, reply, undo/redo, text indent, send, search (if it implies reading direction), progress bars.
|
|
157
|
+
|
|
158
|
+
**Do NOT mirror:** play/pause, checkmarks, plus/minus, clock, globe, user, settings gear, download, external link.
|
|
159
|
+
|
|
160
|
+
```css
|
|
161
|
+
[dir="rtl"] .icon-directional {
|
|
162
|
+
transform: scaleX(-1);
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## 8. Common Mistakes
|
|
169
|
+
|
|
170
|
+
- **Hardcoding `left`/`right` in CSS.** Use logical properties.
|
|
171
|
+
- **Fixed-width buttons.** They overflow in German/Finnish. Use `min-width`.
|
|
172
|
+
- **Testing only in English.** Design breaks with 35% longer strings.
|
|
173
|
+
- **`text-align: left` instead of `text-align: start`.** Breaks RTL.
|
|
174
|
+
- **Mirroring ALL icons in RTL.** Only directional icons should flip.
|
|
175
|
+
- **Hardcoding date formats.** `01/02/2026` means different dates in US vs UK. Use `Intl.DateTimeFormat`.
|
|
176
|
+
- **Not loading CJK fonts.** System fonts for CJK vary wildly. Include Noto Sans.
|
|
177
|
+
- **Ignoring `dir="auto"` on user content.** A Hebrew comment in an English page needs auto-detection.
|