@wippy-fe/theme 0.0.7
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 +64 -0
- package/THEMING.md +316 -0
- package/package.json +35 -0
- package/primevue/accordion.css +58 -0
- package/primevue/autocomplete.css +144 -0
- package/primevue/avatar.css +50 -0
- package/primevue/badge.css +53 -0
- package/primevue/blockui.css +15 -0
- package/primevue/breadcrumb.css +41 -0
- package/primevue/button.css +341 -0
- package/primevue/buttongroup.css +19 -0
- package/primevue/card.css +22 -0
- package/primevue/carousel.css +64 -0
- package/primevue/cascadeselect.css +169 -0
- package/primevue/checkbox.css +84 -0
- package/primevue/chip.css +27 -0
- package/primevue/colorpicker.css +47 -0
- package/primevue/common.css +81 -0
- package/primevue/confirmdialog.css +10 -0
- package/primevue/confirmpopup.css +66 -0
- package/primevue/contextmenu.css +101 -0
- package/primevue/datatable.css +408 -0
- package/primevue/dataview.css +29 -0
- package/primevue/datepicker.css +211 -0
- package/primevue/dialog.css +125 -0
- package/primevue/divider.css +52 -0
- package/primevue/dock.css +84 -0
- package/primevue/drawer.css +94 -0
- package/primevue/fieldset.css +45 -0
- package/primevue/fileupload.css +57 -0
- package/primevue/floatlabel.css +73 -0
- package/primevue/galleria.css +244 -0
- package/primevue/iconfield.css +23 -0
- package/primevue/iftalabel.css +32 -0
- package/primevue/image.css +56 -0
- package/primevue/imagecompare.css +38 -0
- package/primevue/inplace.css +13 -0
- package/primevue/inputgroup.css +67 -0
- package/primevue/inputnumber.css +84 -0
- package/primevue/inputotp.css +9 -0
- package/primevue/inputtext.css +38 -0
- package/primevue/knob.css +37 -0
- package/primevue/listbox.css +79 -0
- package/primevue/megamenu.css +207 -0
- package/primevue/menu.css +51 -0
- package/primevue/menubar.css +169 -0
- package/primevue/message.css +228 -0
- package/primevue/metergroup.css +67 -0
- package/primevue/multiselect.css +143 -0
- package/primevue/orderlist.css +10 -0
- package/primevue/organizationchart.css +71 -0
- package/primevue/overlaybadge.css +13 -0
- package/primevue/paginator.css +58 -0
- package/primevue/panel.css +27 -0
- package/primevue/panelmenu.css +94 -0
- package/primevue/password.css +61 -0
- package/primevue/picklist.css +18 -0
- package/primevue/popover.css +46 -0
- package/primevue/progressbar.css +67 -0
- package/primevue/progressspinner.css +58 -0
- package/primevue/radiobutton.css +93 -0
- package/primevue/rating.css +23 -0
- package/primevue/ripple.css +7 -0
- package/primevue/scrollpanel.css +41 -0
- package/primevue/scrolltop.css +25 -0
- package/primevue/select.css +144 -0
- package/primevue/selectbutton.css +25 -0
- package/primevue/skeleton.css +11 -0
- package/primevue/slider.css +42 -0
- package/primevue/speeddial.css +48 -0
- package/primevue/splitbutton.css +34 -0
- package/primevue/splitter.css +56 -0
- package/primevue/stepper.css +102 -0
- package/primevue/tabs.css +84 -0
- package/primevue/tag.css +38 -0
- package/primevue/tailwind.css +104 -0
- package/primevue/terminal.css +22 -0
- package/primevue/textarea.css +42 -0
- package/primevue/tieredmenu.css +105 -0
- package/primevue/timeline.css +113 -0
- package/primevue/toast.css +172 -0
- package/primevue/togglebutton.css +63 -0
- package/primevue/toggleswitch.css +66 -0
- package/primevue/toolbar.css +12 -0
- package/primevue/tooltip.css +38 -0
- package/primevue/tree.css +103 -0
- package/primevue/treeselect.css +116 -0
- package/primevue/treetable.css +300 -0
- package/primevue-plugin.ts +8 -0
- package/tailwind.config.ts +28 -0
- package/theme-config.css +124 -0
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# @wippy-fe/theme
|
|
2
|
+
|
|
3
|
+
Theme variables, shared Tailwind config, and PrimeVue styling for Wippy web components and applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @wippy-fe/theme
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Setup
|
|
12
|
+
|
|
13
|
+
### 1. Theme variables (all components)
|
|
14
|
+
|
|
15
|
+
Import the CSS variables in your `styles.css`:
|
|
16
|
+
|
|
17
|
+
```css
|
|
18
|
+
@import "@wippy-fe/theme/theme-config.css";
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### 2. Tailwind preset (components using Tailwind)
|
|
22
|
+
|
|
23
|
+
Use the shared preset in your `tailwind.config.ts`:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import themePreset from '@wippy-fe/theme/tailwind.config'
|
|
27
|
+
|
|
28
|
+
export default {
|
|
29
|
+
presets: [themePreset],
|
|
30
|
+
content: ['./src/**/*.{vue,ts}'],
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
This includes `tailwindcss-primeui` (color utilities, semantic classes, animations), `tailwind-scrollbar`, and the secondary color palette.
|
|
35
|
+
|
|
36
|
+
### 3. PrimeVue plugin (components using PrimeVue)
|
|
37
|
+
|
|
38
|
+
Install PrimeVue with the correct config:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { PrimeVuePlugin } from '@wippy-fe/theme/primevue-plugin'
|
|
42
|
+
|
|
43
|
+
// In vueConfig.plugins:
|
|
44
|
+
plugins: [PrimeVuePlugin]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Import PrimeVue component styles in your CSS:
|
|
48
|
+
|
|
49
|
+
```css
|
|
50
|
+
@import "@wippy-fe/theme/primevue/tailwind.css";
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## What's Included
|
|
54
|
+
|
|
55
|
+
| Export | Description |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `@wippy-fe/theme` | Default theme CSS variables (`theme-config.css`) |
|
|
58
|
+
| `@wippy-fe/theme/tailwind.config` | Shared Tailwind preset (PrimeUI + secondary colors + scrollbar) |
|
|
59
|
+
| `@wippy-fe/theme/primevue-plugin` | Vue plugin that installs PrimeVue with `{ theme: 'none' }` |
|
|
60
|
+
| `@wippy-fe/theme/primevue/*` | PrimeVue component CSS files (Tailwind @apply-based) |
|
|
61
|
+
|
|
62
|
+
## Theming Reference
|
|
63
|
+
|
|
64
|
+
See [THEMING.md](./THEMING.md) for the full reference on CSS variables, Tailwind utility classes, dark mode behavior, and PrimeVue component styling.
|
package/THEMING.md
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# Wippy Theming Reference
|
|
2
|
+
|
|
3
|
+
Comprehensive reference for CSS variables, Tailwind utility classes, dark mode behavior, and PrimeVue styling used across Wippy web components and applications.
|
|
4
|
+
|
|
5
|
+
## 1. CSS Variables
|
|
6
|
+
|
|
7
|
+
All variables are defined in `theme-config.css` and set on `:root`. At runtime, the host injects the real theme — these serve as the dev-time fallback and contract.
|
|
8
|
+
|
|
9
|
+
### Primary palette (11 vars)
|
|
10
|
+
|
|
11
|
+
Base: `--p-primary` (default: `rgb(0, 95, 178)`)
|
|
12
|
+
|
|
13
|
+
| Variable | Value |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `--p-primary-50` | `color-mix(in srgb, var(--p-primary) 5%, white)` |
|
|
16
|
+
| `--p-primary-100` | `color-mix(in srgb, var(--p-primary) 10%, white)` |
|
|
17
|
+
| `--p-primary-200` | `color-mix(in srgb, var(--p-primary) 20%, white)` |
|
|
18
|
+
| `--p-primary-300` | `color-mix(in srgb, var(--p-primary) 30%, white)` |
|
|
19
|
+
| `--p-primary-400` | `color-mix(in srgb, var(--p-primary) 40%, white)` |
|
|
20
|
+
| `--p-primary-500` | `var(--p-primary)` (base) |
|
|
21
|
+
| `--p-primary-600` | `color-mix(in srgb, var(--p-primary) 80%, black)` |
|
|
22
|
+
| `--p-primary-700` | `color-mix(in srgb, var(--p-primary) 70%, black)` |
|
|
23
|
+
| `--p-primary-800` | `color-mix(in srgb, var(--p-primary) 60%, black)` |
|
|
24
|
+
| `--p-primary-900` | `color-mix(in srgb, var(--p-primary) 50%, black)` |
|
|
25
|
+
| `--p-primary-950` | `color-mix(in srgb, var(--p-primary) 40%, black)` |
|
|
26
|
+
|
|
27
|
+
### Secondary palette (11 vars)
|
|
28
|
+
|
|
29
|
+
Base: `--p-secondary` (default: `#6f7385`)
|
|
30
|
+
|
|
31
|
+
Same numbered scale pattern as primary (50–950) using `color-mix`.
|
|
32
|
+
|
|
33
|
+
### Surface palette (13 vars)
|
|
34
|
+
|
|
35
|
+
| Variable | Light value | Dark value |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| `--p-surface-0` | `#fff` | `#fff` |
|
|
38
|
+
| `--p-surface-50` | `#fafafa` | `#fafafa` |
|
|
39
|
+
| `--p-surface-100` | `#f4f4f5` | `#f4f4f5` |
|
|
40
|
+
| `--p-surface-200` | `#e4e4e7` | `#e4e4e7` |
|
|
41
|
+
| `--p-surface-300` | `#d4d4d8` | `#d4d4d8` |
|
|
42
|
+
| `--p-surface-400` | `#a1a1aa` | `#a1a1aa` |
|
|
43
|
+
| `--p-surface-500` | `#71717a` | `#71717a` |
|
|
44
|
+
| `--p-surface-600` | `#52525b` | `#545250` (warm) |
|
|
45
|
+
| `--p-surface-700` | `#3f3f46` | `#403e3c` (warm) |
|
|
46
|
+
| `--p-surface-800` | `#27272a` | `#2b2927` (warm) |
|
|
47
|
+
| `--p-surface-900` | `#18181b` | `#1c1a19` (warm) |
|
|
48
|
+
| `--p-surface-950` | `#09090b` | `#0f0e0d` (warm) |
|
|
49
|
+
|
|
50
|
+
**Fixed light-to-dark scale** — 0 is always lightest, 950 always darkest. The scale does NOT flip with dark mode. In dark mode, only 600–950 get warmer undertones.
|
|
51
|
+
|
|
52
|
+
### Semantic variables (15 vars)
|
|
53
|
+
|
|
54
|
+
These **flip with dark mode** — use these for theme-dependent styling.
|
|
55
|
+
|
|
56
|
+
| Variable | Light | Dark |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| `--p-primary-color` | `primary-500` | `primary-400` |
|
|
59
|
+
| `--p-primary-contrast-color` | `surface-0` | `surface-900` |
|
|
60
|
+
| `--p-primary-hover-color` | `primary-600` | `primary-300` |
|
|
61
|
+
| `--p-primary-active-color` | `primary-700` | `primary-200` |
|
|
62
|
+
| `--p-text-color` | `surface-700` | `surface-0` |
|
|
63
|
+
| `--p-text-hover-color` | `surface-800` | `surface-0` |
|
|
64
|
+
| `--p-text-muted-color` | `surface-500` | `surface-400` |
|
|
65
|
+
| `--p-text-hover-muted-color` | `surface-600` | `surface-300` |
|
|
66
|
+
| `--p-content-background` | — | `surface-900` (via host) |
|
|
67
|
+
| `--p-content-border-color` | `surface-200` | `surface-700` |
|
|
68
|
+
| `--p-content-hover-background` | `surface-100` | `surface-800` |
|
|
69
|
+
| `--p-content-hover-color` | `surface-800` | `surface-0` |
|
|
70
|
+
| `--p-highlight-background` | `primary-50` | `primary-400 @ 16%` |
|
|
71
|
+
| `--p-highlight-color` | `primary-700` | `white @ 87%` |
|
|
72
|
+
| `--p-highlight-focus-background` | `primary-100` | `primary-400 @ 24%` |
|
|
73
|
+
| `--p-highlight-focus-color` | `primary-800` | `white @ 87%` |
|
|
74
|
+
| `--p-content-border-radius` | `6px` | `6px` |
|
|
75
|
+
|
|
76
|
+
## 2. Tailwind Utility Classes
|
|
77
|
+
|
|
78
|
+
Provided by `tailwindcss-primeui` plugin (included in the shared Tailwind preset).
|
|
79
|
+
|
|
80
|
+
### Color utilities
|
|
81
|
+
|
|
82
|
+
Work with `bg-`, `text-`, `border-`, `outline-`, `ring-` prefixes:
|
|
83
|
+
|
|
84
|
+
| Class suffix | Maps to |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `primary` | `--p-primary-color` |
|
|
87
|
+
| `primary-emphasis` | `--p-primary-hover-color` |
|
|
88
|
+
| `primary-emphasis-alt` | `--p-primary-active-color` |
|
|
89
|
+
| `primary-contrast` | `--p-primary-contrast-color` |
|
|
90
|
+
| `primary-{0,50,100,...,950}` | Full primary shade range |
|
|
91
|
+
| `surface-{0,50,100,...,950}` | Full surface shade range |
|
|
92
|
+
|
|
93
|
+
### Semantic utilities
|
|
94
|
+
|
|
95
|
+
| Class | Maps to |
|
|
96
|
+
|---|---|
|
|
97
|
+
| `.text-color` | `--p-text-color` |
|
|
98
|
+
| `.text-color-emphasis` | `--p-text-hover-color` |
|
|
99
|
+
| `.text-muted-color` | `--p-text-muted-color` |
|
|
100
|
+
| `.text-muted-color-emphasis` | `--p-text-hover-muted-color` |
|
|
101
|
+
| `.bg-emphasis` | Emphasis background + text |
|
|
102
|
+
| `.bg-highlight` | Highlighted state (selected items, active rows) |
|
|
103
|
+
| `.bg-highlight-emphasis` | Emphasized highlight |
|
|
104
|
+
| `.border-surface` | `--p-content-border-color` |
|
|
105
|
+
| `.rounded-border` | `--p-content-border-radius` |
|
|
106
|
+
|
|
107
|
+
### Animation utilities
|
|
108
|
+
|
|
109
|
+
| Class | Description |
|
|
110
|
+
|---|---|
|
|
111
|
+
| `.animate-fadein` | Fade in |
|
|
112
|
+
| `.animate-fadeout` | Fade out |
|
|
113
|
+
| `.animate-slidedown` | Slide down |
|
|
114
|
+
| `.animate-slideup` | Slide up |
|
|
115
|
+
| `.animate-scalein` | Scale in |
|
|
116
|
+
| `.animate-fadeinleft` | Fade in from left |
|
|
117
|
+
| `.animate-fadeinright` | Fade in from right |
|
|
118
|
+
| `.animate-fadeinup` | Fade in from below |
|
|
119
|
+
| `.animate-fadeindown` | Fade in from above |
|
|
120
|
+
| `.animate-duration-{ms}` | Animation duration |
|
|
121
|
+
| `.animate-delay-{ms}` | Animation delay |
|
|
122
|
+
| `.animate-ease-*` | Easing functions |
|
|
123
|
+
|
|
124
|
+
### Secondary color utilities
|
|
125
|
+
|
|
126
|
+
Added by the shared Tailwind preset via `theme.extend.colors`:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
bg-secondary-{50..950}
|
|
130
|
+
text-secondary-{50..950}
|
|
131
|
+
border-secondary-{50..950}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## 3. Dark Mode
|
|
135
|
+
|
|
136
|
+
Variables switch at `@media (prefers-color-scheme: dark)`. Key changes:
|
|
137
|
+
|
|
138
|
+
- `--p-primary` base shifts from `rgb(0, 95, 178)` to `rgb(0, 125, 178)` (brighter)
|
|
139
|
+
- `--p-primary-color` shifts from `primary-500` to `primary-400`
|
|
140
|
+
- `--p-content-background` shifts to `surface-900` (via host injection)
|
|
141
|
+
- `--p-text-color` shifts from `surface-700` to `surface-0`
|
|
142
|
+
- Surface 600–950 get warmer undertones in dark mode
|
|
143
|
+
|
|
144
|
+
### Rules
|
|
145
|
+
|
|
146
|
+
- **Use semantic variables** (`--p-text-color`, `--p-content-background`) for colors that should flip with dark mode.
|
|
147
|
+
- **Use `surface-0`/`surface-950`** only when you explicitly need the fixed shade regardless of mode.
|
|
148
|
+
- **For derived shades**: `color-mix(in srgb, var(--p-content-background) 85%, var(--p-text-color) 15%)`.
|
|
149
|
+
- **Never use `--p-surface-N`** for theme-dependent colors — the numbered scale does NOT flip.
|
|
150
|
+
|
|
151
|
+
## 4. PrimeVue Styling (opt-in)
|
|
152
|
+
|
|
153
|
+
The `primevue/` directory contains CSS files that style PrimeVue component tags (`.p-button`, `.p-datatable`, etc.) using Tailwind `@apply` directives. These are **only needed if your component uses PrimeVue tags**.
|
|
154
|
+
|
|
155
|
+
### Setup
|
|
156
|
+
|
|
157
|
+
1. Add `primevue` as a dependency
|
|
158
|
+
2. Import `PrimeVuePlugin` from `@wippy-fe/theme/primevue-plugin` — installs PrimeVue with `{ theme: 'none' }`
|
|
159
|
+
3. Import PrimeVue CSS: `@import "@wippy-fe/theme/primevue/tailwind.css"` in your styles
|
|
160
|
+
4. Add `'primeVueCssUrl'` to `hostCssKeys` so the host injects PrimeVue CSS at runtime
|
|
161
|
+
|
|
162
|
+
### How it works
|
|
163
|
+
|
|
164
|
+
The CSS files use theme variables via Tailwind classes. Example from `button.css`:
|
|
165
|
+
|
|
166
|
+
```css
|
|
167
|
+
.p-button {
|
|
168
|
+
@apply bg-primary text-primary-contrast border-primary
|
|
169
|
+
enabled:hover:bg-primary-emphasis enabled:active:bg-primary-emphasis-alt ...
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The `tailwind.css` master file imports all component CSS files organized by category (form, button, data, overlay, menu, panel, file, message, media, misc).
|
|
174
|
+
|
|
175
|
+
### Available PrimeVue component styles
|
|
176
|
+
|
|
177
|
+
**Form**: autocomplete, cascadeselect, checkbox, colorpicker, datepicker, iconfield, iftalabel, inputgroup, inputnumber, inputotp, inputtext, floatlabel, knob, listbox, multiselect, password, radiobutton, rating, select, selectbutton, slider, textarea, togglebutton, toggleswitch, treeselect
|
|
178
|
+
|
|
179
|
+
**Button**: button, buttongroup, speeddial, splitbutton
|
|
180
|
+
|
|
181
|
+
**Data**: datatable, dataview, paginator, picklist, orderlist, organizationchart, timeline, tree, treetable
|
|
182
|
+
|
|
183
|
+
**Overlay**: confirmdialog, confirmpopup, dialog, drawer, popover, tooltip
|
|
184
|
+
|
|
185
|
+
**Menu**: breadcrumb, contextmenu, dock, menu, menubar, megamenu, panelmenu, tieredmenu
|
|
186
|
+
|
|
187
|
+
**Panel**: accordion, card, divider, fieldset, panel, scrollpanel, splitter, stepper, tabs, toolbar
|
|
188
|
+
|
|
189
|
+
**File**: fileupload
|
|
190
|
+
|
|
191
|
+
**Message**: message, toast
|
|
192
|
+
|
|
193
|
+
**Media**: carousel, galleria, image, imagecompare
|
|
194
|
+
|
|
195
|
+
**Misc**: avatar, badge, blockui, chip, inplace, metergroup, overlaybadge, progressbar, progressspinner, ripple, scrolltop, skeleton, tag, terminal
|
|
196
|
+
|
|
197
|
+
## 5. Choosing Custom Colors
|
|
198
|
+
|
|
199
|
+
When overriding `--p-primary`, `--p-secondary`, or the surface palette, follow these guidelines to ensure contrast and readability across both modes.
|
|
200
|
+
|
|
201
|
+
### How the shade scale works
|
|
202
|
+
|
|
203
|
+
The `color-mix()` system blends your base color with white (lighter shades) or black (darker shades). The percentages are fixed:
|
|
204
|
+
|
|
205
|
+
| Shade | Blend |
|
|
206
|
+
|---|---|
|
|
207
|
+
| 50 | 5% base + 95% white |
|
|
208
|
+
| 100 | 10% base + 90% white |
|
|
209
|
+
| 200 | 20% base + 80% white |
|
|
210
|
+
| 300 | 30% base + 70% white |
|
|
211
|
+
| 400 | 40% base + 60% white |
|
|
212
|
+
| **500** | **100% base (no blend)** |
|
|
213
|
+
| 600 | 80% base + 20% black |
|
|
214
|
+
| 700 | 70% base + 30% black |
|
|
215
|
+
| 800 | 60% base + 40% black |
|
|
216
|
+
| 900 | 50% base + 50% black |
|
|
217
|
+
| 950 | 40% base + 60% black |
|
|
218
|
+
|
|
219
|
+
This means the base color's luminosity determines the contrast of the **entire scale**. A too-light base produces a washed-out 500 that can't contrast against white text; a too-dark base makes 400 too dim for dark mode.
|
|
220
|
+
|
|
221
|
+
### Primary color luminosity
|
|
222
|
+
|
|
223
|
+
The primary base is used at two critical contrast points:
|
|
224
|
+
|
|
225
|
+
1. **Light mode**: `bg-primary` (500) with `text-primary-contrast` (white) — buttons, badges, checkboxes
|
|
226
|
+
2. **Dark mode**: `bg-primary` (400 = 40% base + 60% white) with `text-primary-contrast` (surface-900, near-black)
|
|
227
|
+
|
|
228
|
+
**Recommended relative luminance for `--p-primary`: 0.05–0.15** (medium-dark, saturated).
|
|
229
|
+
|
|
230
|
+
| Example | RGB | Rel. luminance | Light contrast (500 vs white) | Dark contrast (400 vs black) |
|
|
231
|
+
|---|---|---|---|---|
|
|
232
|
+
| Default blue | `rgb(0, 95, 178)` | ~0.10 | ~8.5:1 (AAA) | ~6.8:1 (AA) |
|
|
233
|
+
| Deep teal | `rgb(0, 128, 128)` | ~0.14 | ~5.8:1 (AA) | ~8.7:1 (AAA) |
|
|
234
|
+
| Indigo | `rgb(79, 70, 229)` | ~0.07 | ~8.0:1 (AAA) | ~5.5:1 (AA) |
|
|
235
|
+
| Red | `rgb(185, 28, 28)` | ~0.05 | ~9.2:1 (AAA) | ~4.7:1 (borderline) |
|
|
236
|
+
| Forest green | `rgb(22, 101, 52)` | ~0.08 | ~7.2:1 (AAA) | ~6.1:1 (AA) |
|
|
237
|
+
| Orange | `rgb(194, 120, 3)` | ~0.18 | ~3.7:1 (FAIL) | ~9.5:1 (AAA) |
|
|
238
|
+
|
|
239
|
+
**Key constraints:**
|
|
240
|
+
- Luminance **> 0.18** → 500 shade is too light for white text (fails WCAG AA 4.5:1 in light mode)
|
|
241
|
+
- Luminance **< 0.04** → 400 shade is too dark for dark-on-light text (fails WCAG AA in dark mode)
|
|
242
|
+
- The sweet spot is **0.06–0.14** — both modes pass WCAG AA (4.5:1), with most passing AAA (7:1)
|
|
243
|
+
- Pure, saturated colors (high chroma) work best. Desaturated/muted colors lose contrast faster
|
|
244
|
+
|
|
245
|
+
**If your brand color is too light** (e.g., orange, yellow, lime): darken it for `--p-primary` and use the original as a secondary or accent. Light-mode buttons with white text will fail contrast otherwise.
|
|
246
|
+
|
|
247
|
+
**If your brand color is too dark** (e.g., navy, dark brown): lighten it slightly. The 400 shade in dark mode needs enough contrast against `surface-900`.
|
|
248
|
+
|
|
249
|
+
### Secondary color luminosity
|
|
250
|
+
|
|
251
|
+
Secondary is used for muted/deemphasized elements — less critical than primary but still appears in text and borders.
|
|
252
|
+
|
|
253
|
+
**Recommended relative luminance for `--p-secondary`: 0.10–0.25** (mid-range, desaturated).
|
|
254
|
+
|
|
255
|
+
The default `#6f7385` (luminance ~0.16) is a muted gray-purple that reads as neutral in both modes. Good secondary colors:
|
|
256
|
+
- Are **less saturated** than primary (avoid competing for attention)
|
|
257
|
+
- Have **mid-range luminance** so the full shade scale is usable
|
|
258
|
+
- Work as `text-secondary-600` on white and `text-secondary-300` on dark backgrounds
|
|
259
|
+
|
|
260
|
+
### Surface palette
|
|
261
|
+
|
|
262
|
+
Surfaces are a fixed grayscale ramp — they do NOT flip with dark mode. The semantic variables (`--p-content-background`, `--p-text-color`) reference different points on this scale per mode.
|
|
263
|
+
|
|
264
|
+
**Light mode assignments:**
|
|
265
|
+
|
|
266
|
+
| Role | Surface | Hex | Purpose |
|
|
267
|
+
|---|---|---|---|
|
|
268
|
+
| Page background | 0 | `#ffffff` | Main canvas |
|
|
269
|
+
| Card/container background | 0 | `#ffffff` | Content containers |
|
|
270
|
+
| Hover background | 100 | `#f4f4f5` | Interactive hover state |
|
|
271
|
+
| Border | 200 | `#e4e4e7` | Borders, dividers |
|
|
272
|
+
| Muted text | 500 | `#71717a` | Secondary text, labels |
|
|
273
|
+
| Body text | 700 | `#3f3f46` | Primary readable text |
|
|
274
|
+
| Strong emphasis | 800 | `#27272a` | Headings, hover text |
|
|
275
|
+
|
|
276
|
+
**Dark mode assignments:**
|
|
277
|
+
|
|
278
|
+
| Role | Surface | Hex | Purpose |
|
|
279
|
+
|---|---|---|---|
|
|
280
|
+
| Page background | 900 | `#1c1a19` (warm) | Main canvas |
|
|
281
|
+
| Card/container background | 800 | `#2b2927` (warm) | Elevated containers |
|
|
282
|
+
| Hover background | 800 | `#2b2927` (warm) | Interactive hover state |
|
|
283
|
+
| Border | 700 | `#403e3c` (warm) | Borders, dividers |
|
|
284
|
+
| Muted text | 400 | `#a1a1aa` | Secondary text, labels |
|
|
285
|
+
| Body text | 0 | `#ffffff` | Primary readable text |
|
|
286
|
+
| Strong emphasis | 0 | `#ffffff` | Headings, hover text |
|
|
287
|
+
|
|
288
|
+
**Dark mode warm tones** (600–950): The default surfaces shift from cool zinc grays to slightly warm brown-grays in dark mode. This reduces visual fatigue on dark backgrounds. The shift is subtle — `#545250` vs `#52525b` for surface-600 — but creates a warmer, more natural feel.
|
|
289
|
+
|
|
290
|
+
When providing custom surfaces, keep these rules:
|
|
291
|
+
- **0 must always be `#ffffff`** (or very near it) — used as contrast text in dark mode
|
|
292
|
+
- **950 must always be very dark** — used as the maximum-contrast anchor
|
|
293
|
+
- **The scale must increase monotonically in darkness** — no inversions
|
|
294
|
+
- **600–950 should shift warm** in dark mode for visual comfort (optional but recommended)
|
|
295
|
+
- **Don't change 0–500** between modes unless you have a specific reason — the semantic variables handle mode switching
|
|
296
|
+
|
|
297
|
+
### Contrast verification
|
|
298
|
+
|
|
299
|
+
After choosing colors, verify these critical pairs meet WCAG AA (4.5:1 minimum):
|
|
300
|
+
|
|
301
|
+
| Pair | Light mode | Dark mode |
|
|
302
|
+
|---|---|---|
|
|
303
|
+
| `bg-primary` + `text-primary-contrast` | primary-500 vs white | primary-400 vs surface-900 |
|
|
304
|
+
| `bg-highlight` + text | primary-50 vs primary-700 | primary-400@16% vs white@87% |
|
|
305
|
+
| Body text on background | surface-700 vs white | surface-0 vs surface-900 |
|
|
306
|
+
| Muted text on background | surface-500 vs white | surface-400 vs surface-900 |
|
|
307
|
+
|
|
308
|
+
Quick formula for relative luminance of an sRGB color:
|
|
309
|
+
```
|
|
310
|
+
L = 0.2126 * R' + 0.7152 * G' + 0.0722 * B'
|
|
311
|
+
where R' = (R/255)^2.2 (simplified gamma)
|
|
312
|
+
|
|
313
|
+
Contrast ratio = (L_lighter + 0.05) / (L_darker + 0.05)
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
WCAG AA requires 4.5:1 for normal text, 3:1 for large text (18px+ bold or 24px+ regular).
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wippy-fe/theme",
|
|
3
|
+
"version": "0.0.7",
|
|
4
|
+
"description": "Theme variables, shared Tailwind config, and PrimeVue styling for Wippy web components.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "theme-config.css",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./theme-config.css",
|
|
9
|
+
"./theme-config.css": "./theme-config.css",
|
|
10
|
+
"./tailwind.config": "./tailwind.config.ts",
|
|
11
|
+
"./primevue-plugin": "./primevue-plugin.ts",
|
|
12
|
+
"./primevue/*": "./primevue/*"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"theme-config.css",
|
|
16
|
+
"tailwind.config.ts",
|
|
17
|
+
"primevue-plugin.ts",
|
|
18
|
+
"primevue/",
|
|
19
|
+
"THEMING.md",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"tailwindcss-primeui": "^0.6.1",
|
|
24
|
+
"tailwind-scrollbar": "^3.0.0"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"primevue": "^4.0.0"
|
|
28
|
+
},
|
|
29
|
+
"peerDependenciesMeta": {
|
|
30
|
+
"primevue": {
|
|
31
|
+
"optional": true
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"license": "UNLICENSED"
|
|
35
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
.p-accordionpanel {
|
|
2
|
+
@apply flex flex-col border-b border-surface-200 dark:border-surface-700
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.p-accordionheader {
|
|
6
|
+
@apply cursor-pointer disabled:cursor-auto flex items-center justify-between p-[1.125rem] font-semibold
|
|
7
|
+
bg-surface-0 dark:bg-surface-900
|
|
8
|
+
text-surface-500 dark:text-surface-400
|
|
9
|
+
transition-colors duration-200
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.p-accordionpanel:first-child > .p-accordionheader {
|
|
13
|
+
@apply rounded-ss-md rounded-se-md
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.p-accordionpanel:last-child > .p-accordionheader {
|
|
17
|
+
@apply rounded-ee-md rounded-es-md
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.p-accordionpanel:last-child.p-accordionpanel-active > .p-accordionheader {
|
|
21
|
+
@apply rounded-ee-md rounded-es-md
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.p-accordionheader-toggle-icon {
|
|
25
|
+
@apply text-surface-500 dark:text-surface-400
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.p-accordionpanel:not(.p-disabled) .p-accordionheader:focus-visible {
|
|
29
|
+
@apply outline outline-1 outline-offset-[-1px] outline-primary
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.p-accordionpanel:not(.p-accordionpanel-active):not(.p-disabled) > .p-accordionheader:hover {
|
|
33
|
+
@apply bg-surface-0 dark:bg-surface-900 text-surface-700 dark:text-surface-0
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.p-accordionpanel:not(.p-accordionpanel-active):not(.p-disabled) .p-accordionheader:hover .p-accordionheader-toggle-icon {
|
|
37
|
+
@apply text-surface-700 dark:text-surface-0
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.p-accordionpanel:not(.p-disabled).p-accordionpanel-active > .p-accordionheader {
|
|
41
|
+
@apply bg-surface-0 dark:bg-surface-900 text-surface-700 dark:text-surface-0
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.p-accordionpanel:not(.p-disabled).p-accordionpanel-active > .p-accordionheader .p-accordionheader-toggle-icon {
|
|
45
|
+
@apply text-surface-700 dark:text-surface-0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.p-accordionpanel:not(.p-disabled).p-accordionpanel-active > .p-accordionheader:hover {
|
|
49
|
+
@apply bg-surface-0 dark:bg-surface-900 text-surface-700 dark:text-surface-0
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.p-accordionpanel:not(.p-disabled).p-accordionpanel-active > .p-accordionheader:hover .p-accordionheader-toggle-icon {
|
|
53
|
+
@apply text-surface-700 dark:text-surface-0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.p-accordioncontent-content {
|
|
57
|
+
@apply bg-surface-0 dark:bg-surface-900 text-surface-700 dark:text-surface-0 pt-0 px-[1.125rem] pb-[1.125rem]
|
|
58
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
@import './inputtext';
|
|
2
|
+
@import './chip';
|
|
3
|
+
|
|
4
|
+
.p-autocomplete {
|
|
5
|
+
@apply inline-flex
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.p-autocomplete.p-disabled {
|
|
9
|
+
@apply opacity-100
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.p-autocomplete-loader {
|
|
13
|
+
@apply absolute top-1/2 -mt-2 end-3
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
.p-autocomplete:has(.p-autocomplete-dropdown) .p-autocomplete-loader {
|
|
18
|
+
@apply end-[3.25rem]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.p-autocomplete:has(.p-autocomplete-dropdown) .p-autocomplete-input {
|
|
22
|
+
@apply flex-auto w-[1%]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.p-autocomplete:has(.p-autocomplete-dropdown) .p-autocomplete-input,
|
|
26
|
+
.p-autocomplete:has(.p-autocomplete-dropdown) .p-autocomplete-input-multiple {
|
|
27
|
+
@apply rounded-e-none
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.p-autocomplete-dropdown {
|
|
31
|
+
@apply cursor-pointer inline-flex items-center justify-center select-none overflow-hidden relative w-10 rounded-e-md
|
|
32
|
+
bg-surface-100 enabled:hover:bg-surface-200 enabled:active:bg-surface-300
|
|
33
|
+
text-surface-600 enabled:hover:text-surface-700 enabled:hover:active:text-surface-800
|
|
34
|
+
dark:bg-surface-800 dark:enabled:hover:bg-surface-700 dark:enabled:active:bg-surface-600
|
|
35
|
+
dark:text-surface-300 dark:enabled:hover:text-surface-200 dark:enabled:active:text-surface-100
|
|
36
|
+
focus-visible:outline focus-visible:outline-1 focus-visible:outline-offset-2 focus-visible:outline-primary
|
|
37
|
+
transition-colors duration-200
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.p-autocomplete .p-autocomplete-overlay {
|
|
41
|
+
@apply min-w-full
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.p-autocomplete-overlay {
|
|
45
|
+
@apply absolute top-0 left-0 rounded-md
|
|
46
|
+
bg-surface-0 dark:bg-surface-900
|
|
47
|
+
border border-surface-200 dark:border-surface-700
|
|
48
|
+
text-surface-700 dark:text-surface-0
|
|
49
|
+
shadow-[0_4px_6px_-1px_rgba(0,0,0,0.1),0_2px_4px_-2px_rgba(0,0,0,0.1)]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.p-autocomplete-list-container {
|
|
53
|
+
@apply overflow-auto
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.p-autocomplete-list {
|
|
57
|
+
@apply m-0 p-1 list-none flex flex-col gap-[2px]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.p-autocomplete-option {
|
|
61
|
+
@apply cursor-pointer whitespace-nowrap relative overflow-hidden flex items-center px-3 py-2 rounded-sm
|
|
62
|
+
text-surface-700 dark:text-surface-0 bg-transparent border-none
|
|
63
|
+
transition-colors duration-200
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.p-autocomplete-option:not(.p-autocomplete-option-selected):not(.p-disabled).p-focus {
|
|
67
|
+
@apply bg-surface-100 dark:bg-surface-800 text-surface-700 dark:text-surface-0
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.p-autocomplete-option-selected {
|
|
71
|
+
@apply bg-highlight
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.p-autocomplete-option-selected.p-focus {
|
|
75
|
+
@apply bg-highlight-emphasis
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.p-autocomplete-option-group {
|
|
79
|
+
@apply m-0 px-3 py-2 text-surface-500 dark:text-surface-400 font-semibold bg-transparent
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.p-autocomplete-input-multiple {
|
|
83
|
+
@apply m-0 list-none cursor-text overflow-hidden flex items-center flex-wrap
|
|
84
|
+
px-3 py-1 gap-1 text-surface-700 dark:text-surface-0 bg-surface-0 dark:bg-surface-950
|
|
85
|
+
border border-surface-300 dark:border-surface-700 rounded-md w-full
|
|
86
|
+
shadow-[0_1px_2px_0_rgba(18,18,23,0.05)]
|
|
87
|
+
transition-colors duration-200 outline-none
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.p-autocomplete:not(.p-disabled):hover .p-autocomplete-input-multiple {
|
|
91
|
+
@apply border-surface-400 dark:border-surface-600
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.p-autocomplete:not(.p-disabled).p-focus .p-autocomplete-input-multiple {
|
|
95
|
+
@apply border-primary
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.p-autocomplete.p-invalid .p-autocomplete-input-multiple {
|
|
99
|
+
@apply border-red-400 dark:border-red-300
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.p-variant-filled.p-autocomplete-input-multiple {
|
|
103
|
+
@apply bg-surface-50 dark:bg-surface-800
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.p-autocomplete.p-disabled .p-autocomplete-input-multiple {
|
|
107
|
+
@apply opacity-100 cursor-default bg-surface-200 text-surface-500 dark:bg-surface-700 dark:text-surface-400
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.p-autocomplete-chip.p-chip {
|
|
111
|
+
@apply py-1 rounded-sm
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.p-autocomplete-input-multiple:has(.p-autocomplete-chip) {
|
|
115
|
+
@apply px-1
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.p-autocomplete-chip-item.p-focus .p-autocomplete-chip {
|
|
119
|
+
@apply bg-surface-200 text-surface-800 dark:bg-surface-700 dark:text-surface-0
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.p-autocomplete-input-chip {
|
|
123
|
+
@apply flex-auto inline-flex py-1
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.p-autocomplete-input-chip input {
|
|
127
|
+
@apply border-none outline-none bg-transparent m-0 p-0 shadow-none rounded-none w-full text-inherit
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.p-autocomplete-input-chip input::placeholder {
|
|
131
|
+
@apply text-surface-500 dark:text-surface-400
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.p-autocomplete-empty-message {
|
|
135
|
+
@apply px-3 py-2
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.p-autocomplete-fluid {
|
|
139
|
+
@apply flex
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.p-autocomplete-fluid:has(.p-autocomplete-dropdown) .p-autocomplete-input {
|
|
143
|
+
@apply w-[1%]
|
|
144
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
.p-avatar {
|
|
2
|
+
@apply inline-flex items-center justify-center
|
|
3
|
+
w-8 h-8 text-base rounded-md
|
|
4
|
+
bg-surface-200 dark:bg-surface-700
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.p-avatar-image {
|
|
8
|
+
@apply bg-transparent
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.p-avatar-circle,
|
|
12
|
+
.p-avatar-circle img {
|
|
13
|
+
@apply rounded-full
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.p-avatar-icon {
|
|
17
|
+
@apply text-base
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.p-avatar img {
|
|
21
|
+
@apply w-full h-full
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.p-avatar-lg {
|
|
25
|
+
@apply w-12 h-12 text-2xl
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.p-avatar-lg .p-avatar-icon {
|
|
29
|
+
@apply text-2xl
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.p-avatar-xl {
|
|
33
|
+
@apply w-16 h-16 text-[2rem]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.p-avatar-xl .p-avatar-icon {
|
|
37
|
+
@apply text-[2rem]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.p-avatar-group {
|
|
41
|
+
@apply flex items-center
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.p-avatar-group .p-avatar + .p-avatar {
|
|
45
|
+
@apply -ms-4
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.p-avatar-group .p-avatar {
|
|
49
|
+
@apply border-2 border-surface-200 dark:border-surface-700
|
|
50
|
+
}
|