@codenhub/styles 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,34 @@
1
+ # Roadmap
2
+
3
+ **Status:** APPROVED
4
+ **Last updated:** 2026-06-23
5
+ **Scope:** `@codenhub/styles` package direction.
6
+
7
+ ## Purpose
8
+
9
+ This roadmap tracks durable direction for `@codenhub/styles`. It captures styling-system improvements that should guide future changes without turning this document into a release checklist.
10
+
11
+ ## Recently Completed
12
+
13
+ - Published the v0.0.2 foundation: design tokens (colors, layout, radius, motion, focus, shadows, layering, dark mode), presentation/helper classes (buttons, forms, feedback, loaders, tooltips), accessibility contrast verification, and multi-path bundler visual regression tests.
14
+
15
+ ## Planned
16
+
17
+ - **Aesthetic Themes**: Add aesthetic themes (e.g., glassmorphism, brutalism, glitch) to allow changing the whole look-and-feel of the UI.
18
+ - **Layout Helpers**: Expand layout helpers to support common page structure and flex/grid requirements.
19
+ - **Dynamic Tokens & Color Schemes**: Add `@codenhub/theme` as a dev dependency to enable dynamic token changes and color scheme creation.
20
+ - **Button Presentation Classes**: Consider adding more presentation classes for buttons (e.g., loading state enhancements) once component APIs stabilize.
21
+ - **Compatibility Tests**: Add more explicit compatibility tests for intent and presentation combinations when new combinations are supported.
22
+
23
+ ## Not Planned
24
+
25
+ - **JS/TS Helpers**: Runtime DOM helpers (such as a typed `createElement` wrapper) are not planned for now. They will be considered further before any future package expansion, but remain out of scope for now to keep the package CSS-only.
26
+ - Public JavaScript behavior like toast dismissal, focus management, and app-level theme state remain outside the CSS package.
27
+
28
+ ## References
29
+
30
+ - [Overview](./index.md)
31
+ - [Tokens](./tokens.md)
32
+ - [Classes](./classes.md)
33
+ - [Accessibility](./accessibility.md)
34
+ - [Tests](./tests.md)
@@ -33,6 +33,10 @@ Tokens are CSS custom properties. Default values apply by default. `.dark` may o
33
33
  | `--color-surface` | Nested or muted surface. |
34
34
  | `--color-text` | Primary text color. |
35
35
  | `--color-text-secondary` | Secondary text color. |
36
+ | `--color-text-contrast` | Text/icon color on text filled surface. |
37
+ | `--color-text-hover` | Text hover state. |
38
+ | `--color-text-subtle` | Low-emphasis companion tone for text. Use as soft surface or subtle background. |
39
+ | `--color-text-strong` | High-emphasis companion tone for text. Use as readable text, icon, or border on subtle surfaces. |
36
40
  | `--color-success` | Success state color. |
37
41
  | `--color-success-contrast` | Text/icon color on success filled surface. Meets 3:1 UI component contrast against `--color-success`. |
38
42
  | `--color-success-hover` | Success hover tone. |
@@ -60,38 +64,35 @@ Tokens are CSS custom properties. Default values apply by default. `.dark` may o
60
64
 
61
65
  Foundation tokens are not aliases for one color. They define layout, shape, motion, focus, depth, and layering behavior used by helper classes.
62
66
 
63
- | Token | Purpose |
64
- | -------------------------- | ---------------------------------------------------------------------------- |
65
- | `--font-default` | Default app font family. |
66
- | `--container-narrow` | Narrow content width. |
67
- | `--container-max` | Default content width. |
68
- | `--container-wide` | Wide content width. |
69
- | `--layout-gutter` | Responsive inline page padding. |
70
- | `--layout-section-block` | Responsive section vertical padding. |
71
- | `--layout-stack-gap` | Default vertical stack gap. |
72
- | `--layout-cluster-gap` | Default horizontal/wrapped cluster gap. |
73
- | `--layout-grid-min` | Minimum column width for `.auto-grid`. |
74
- | `--radius-control` | Radius for buttons, inputs, tooltips, and compact UI. |
75
- | `--radius-surface` | Radius for cards, panels, and alerts. |
76
- | `--radius-overlay` | Radius for overlays such as toasts. |
77
- | `--control-height` | Default minimum height for controls. |
78
- | `--border-width` | Default border width. |
79
- | `--elevation-low` | Elevation for surface-level components such as cards and panels. |
80
- | `--elevation-mid` | Elevation for elevated components such as dropdowns and floating containers. |
81
- | `--elevation-high` | Elevation for tooltips and inline overlays. |
82
- | `--elevation-overlay` | Elevation for full-screen overlays and toasts. |
83
- | `--focus-ring` | Focus-visible ring color. |
84
- | `--focus-ring-offset` | Focus-visible outline offset. |
85
- | `--focus-ring-width` | Focus-visible outline/ring width. |
86
- | `--motion-duration-fast` | Fast transition for buttons and input interactions (`120ms`). |
87
- | `--motion-duration-normal` | Normal transition for layout, modals, and banners (`200ms`). |
88
- | `--motion-duration-slow` | Slow animation for skeleton loaders and progress bars (`400ms`). |
89
- | `--motion-ease` | Default easing curve. |
90
- | `--backdrop-overlay` | Backdrop filter value for overlay-like surfaces. |
91
- | `--z-popover` | Popover/tooltip z-index. |
92
- | `--z-toast` | Toast z-index. |
93
- | `--breakpoint-xs` | Extra-small Tailwind responsive breakpoint. |
94
- | `--breakpoint-2xl` | Extended large Tailwind responsive breakpoint. |
67
+ | Token | Purpose |
68
+ | --------------------------- | ---------------------------------------------------------------------------- |
69
+ | `--font-default` | Default app font family. |
70
+ | `--container-narrow` | Narrow content width. |
71
+ | `--container-max` | Default content width. |
72
+ | `--container-wide` | Wide content width. |
73
+ | `--layout-gutter` | Responsive inline page padding. |
74
+ | `--layout-section-block` | Responsive section vertical padding. |
75
+ | `--layout-stack-gap` | Default vertical stack gap. |
76
+ | `--layout-cluster-gap` | Default horizontal/wrapped cluster gap. |
77
+ | `--layout-grid-min` | Minimum column width for `.auto-grid`. |
78
+ | `--radius-control` | Radius for buttons, inputs, tooltips, and compact UI. |
79
+ | `--radius-surface` | Radius for cards, panels, and alerts. |
80
+ | `--control-height` | Default minimum height for controls. |
81
+ | `--border-width` | Default border width. |
82
+ | `--elevation-low` | Elevation for surface-level components such as cards and panels. |
83
+ | `--elevation-mid` | Elevation for elevated components such as dropdowns and floating containers. |
84
+ | `--elevation-high` | Elevation for tooltips and inline overlays. |
85
+ | `--focus-ring` | Focus-visible ring color. |
86
+ | `--focus-ring-offset` | Focus-visible outline offset. |
87
+ | `--focus-ring-width` | Focus-visible outline/ring width. |
88
+ | `--motion-duration-fast` | Fast transition for buttons and input interactions (`120ms`). |
89
+ | `--motion-duration-normal` | Normal transition for layout and modals (`200ms`). |
90
+ | `--motion-duration-slow` | Slow animation for skeleton loaders and progress bars (`400ms`). |
91
+ | `--motion-ease` | Default easing curve. |
92
+ | `--z-popover` | Popover/tooltip z-index. |
93
+ | `--surface-hover-transform` | The transform applied on interactive card/panel hover. |
94
+ | `--breakpoint-xs` | Extra-small Tailwind responsive breakpoint. |
95
+ | `--breakpoint-2xl` | Extended large Tailwind responsive breakpoint. |
95
96
 
96
97
  ## Component Internals
97
98
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codenhub/styles",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "private": false,
5
5
  "description": "CSS-only Codenhub design tokens, base styles, and composable UI helper classes.",
6
6
  "homepage": "https://github.com/codenhub/codenhub/tree/main/packages/styles",
@@ -11,7 +11,7 @@
11
11
  "directory": "packages/styles"
12
12
  },
13
13
  "files": [
14
- ".docs",
14
+ "docs",
15
15
  "dist",
16
16
  "src"
17
17
  ],
@@ -22,7 +22,14 @@
22
22
  "./components": "./dist/components.css",
23
23
  "./tw": "./src/index.css",
24
24
  "./tw/theme": "./src/theme.css",
25
- "./tw/components": "./src/components.css"
25
+ "./tw/components": "./src/components/index.css",
26
+ "./tw/surface": "./src/components/surface.css",
27
+ "./tw/button": "./src/components/button.css",
28
+ "./tw/form": "./src/components/form.css",
29
+ "./tw/feedback": "./src/components/feedback.css",
30
+ "./tw/loader": "./src/components/loader.css",
31
+ "./tw/tooltip": "./src/components/tooltip.css",
32
+ "./tw/utilities": "./src/utilities.css"
26
33
  },
27
34
  "publishConfig": {
28
35
  "access": "public"
@@ -45,7 +52,7 @@
45
52
  "scripts": {
46
53
  "dev": "node ./scripts/dev.js",
47
54
  "build": "pnpm build:styles",
48
- "build:styles": "tailwindcss -i ./src/index.css -o ./dist/index.css --minify && tailwindcss -i ./src/theme.css -o ./dist/theme.css --minify && tailwindcss -i ./src/components.css -o ./dist/components.css --minify",
55
+ "build:styles": "tailwindcss -i ./src/index.css -o ./dist/index.css --minify && tailwindcss -i ./src/theme.css -o ./dist/theme.css --minify && tailwindcss -i ./src/components/index.css -o ./dist/components.css --minify",
49
56
  "status:npm": "npm view @codenhub/styles version dist-tags time --json && npm dist-tag ls @codenhub/styles && npm access get status @codenhub/styles",
50
57
  "status:pack": "npm pack --dry-run",
51
58
  "test": "pnpm typecheck && pnpm test:build && pnpm test:visual",
@@ -0,0 +1,155 @@
1
+ @import "tailwindcss";
2
+ @import "../theme.css";
3
+
4
+ @utility btn {
5
+ --button-color: var(--color-text);
6
+ --button-contrast: var(--color-text-contrast);
7
+ --button-hover: var(--color-text-hover);
8
+ --button-subtle: var(--color-text-subtle);
9
+ --button-strong: var(--color-text-strong);
10
+ --button-bg: var(--button-color);
11
+ --button-fg: var(--button-contrast);
12
+ --button-border: var(--button-bg);
13
+ --button-hover-bg: var(--button-hover);
14
+ --button-hover-fg: var(--button-fg);
15
+ --button-hover-border: var(--button-hover-bg);
16
+ --button-text: var(--button-strong);
17
+ --button-soft-bg: var(--button-subtle);
18
+ --button-subtle-hover-bg: color-mix(in srgb, var(--button-soft-bg) 80%, transparent);
19
+ --button-soft-hover-bg: color-mix(in srgb, var(--button-soft-bg) 84%, var(--button-text));
20
+ --button-spinner: var(--button-fg);
21
+
22
+ @apply relative inline-flex min-h-(--control-height,2.5rem) w-fit items-center justify-center gap-2 rounded-(--radius-control) bg-(--button-bg) px-4 py-2 font-semibold whitespace-nowrap text-(--button-fg) no-underline transition-[background-color,border-color,color,opacity,transform] duration-(--motion-duration-fast) ease-(--motion-ease) select-none;
23
+ border: var(--border-width) solid var(--button-border);
24
+
25
+ &.primary {
26
+ --button-color: var(--color-primary);
27
+ --button-contrast: var(--color-primary-contrast);
28
+ --button-hover: var(--color-primary-hover);
29
+ --button-subtle: var(--color-primary-subtle);
30
+ --button-strong: var(--color-primary-strong);
31
+ }
32
+
33
+ &.secondary {
34
+ --button-color: var(--color-accent);
35
+ --button-contrast: var(--color-accent-contrast);
36
+ --button-hover: var(--color-accent-hover);
37
+ --button-subtle: var(--color-accent-subtle);
38
+ --button-strong: var(--color-accent-strong);
39
+ }
40
+
41
+ &.success {
42
+ --button-color: var(--color-success);
43
+ --button-contrast: var(--color-success-contrast);
44
+ --button-hover: var(--color-success-hover);
45
+ --button-subtle: var(--color-success-subtle);
46
+ --button-strong: var(--color-success-strong);
47
+ }
48
+
49
+ &.warning {
50
+ --button-color: var(--color-warning);
51
+ --button-contrast: var(--color-warning-contrast);
52
+ --button-hover: var(--color-warning-hover);
53
+ --button-subtle: var(--color-warning-subtle);
54
+ --button-strong: var(--color-warning-strong);
55
+ }
56
+
57
+ &.destructive,
58
+ &.danger,
59
+ &.error {
60
+ --button-color: var(--color-destructive);
61
+ --button-contrast: var(--color-destructive-contrast);
62
+ --button-hover: var(--color-destructive-hover);
63
+ --button-subtle: var(--color-destructive-subtle);
64
+ --button-strong: var(--color-destructive-strong);
65
+ }
66
+
67
+ &.info {
68
+ --button-color: var(--color-info);
69
+ --button-contrast: var(--color-info-contrast);
70
+ --button-hover: var(--color-info-hover);
71
+ --button-subtle: var(--color-info-subtle);
72
+ --button-strong: var(--color-info-strong);
73
+ }
74
+
75
+ &.out {
76
+ --button-spinner: var(--button-text);
77
+
78
+ @apply bg-transparent text-(--button-color);
79
+ border: calc(var(--border-width) * 2) solid var(--button-color);
80
+
81
+ &:not(:disabled, .disabled, [aria-disabled="true"], [data-disabled]):hover {
82
+ @apply border-(--button-color) bg-(--button-subtle) text-(--button-color);
83
+ }
84
+
85
+ &.fill {
86
+ &:not(:disabled, .disabled, [aria-disabled="true"], [data-disabled]):hover {
87
+ @apply border-(--button-color) bg-(--button-color) text-(--button-contrast);
88
+ }
89
+ }
90
+ }
91
+
92
+ &.ghost {
93
+ --button-spinner: var(--button-text);
94
+
95
+ @apply border-transparent bg-transparent text-(--button-text);
96
+
97
+ &:not(:disabled, .disabled, [aria-disabled="true"], [data-disabled]):hover {
98
+ @apply border-transparent bg-(--button-subtle-hover-bg) text-(--button-text);
99
+ }
100
+ }
101
+
102
+ &.soft {
103
+ --button-spinner: var(--button-text);
104
+
105
+ @apply border-transparent bg-(--button-soft-bg) text-(--button-text);
106
+
107
+ &:not(:disabled, .disabled, [aria-disabled="true"], [data-disabled]):hover {
108
+ @apply border-transparent bg-(--button-soft-hover-bg) text-(--button-text);
109
+ }
110
+ }
111
+
112
+ &.sm {
113
+ @apply min-h-8 px-3 py-1 text-sm;
114
+ }
115
+
116
+ &.lg {
117
+ @apply min-h-12 px-5 py-2.5 text-[1.0625rem];
118
+ }
119
+
120
+ &.icon {
121
+ @apply aspect-square min-w-(--control-height,2.5rem) p-2;
122
+
123
+ aspect-ratio: 1;
124
+ }
125
+
126
+ &.pill {
127
+ @apply rounded-full;
128
+ }
129
+
130
+ &:not(:disabled, .disabled, [aria-disabled="true"], [data-disabled]):hover {
131
+ &:not(.out, .ghost, .soft) {
132
+ @apply border-(--button-hover-border) bg-(--button-hover-bg) text-(--button-hover-fg);
133
+ }
134
+ }
135
+
136
+ &:disabled,
137
+ &.disabled,
138
+ &[aria-disabled="true"],
139
+ &[data-disabled] {
140
+ @apply cursor-not-allowed;
141
+
142
+ opacity: 0.58;
143
+ }
144
+
145
+ &.loading {
146
+ @apply pointer-events-none text-transparent! transition-[opacity,transform];
147
+
148
+ &::after {
149
+ @apply absolute top-[calc(50%+var(--border-width))] left-[calc(50%+var(--border-width))] block size-4 -translate-1/2 rounded-full bg-(--button-spinner);
150
+
151
+ content: "";
152
+ @apply ai;
153
+ }
154
+ }
155
+ }
@@ -0,0 +1,214 @@
1
+ @import "tailwindcss";
2
+ @import "../theme.css";
3
+
4
+ @keyframes anim-skeleton {
5
+ 0% {
6
+ background-position: 200% 0;
7
+ }
8
+
9
+ 100% {
10
+ background-position: -200% 0;
11
+ }
12
+ }
13
+
14
+ @utility alert {
15
+ --feedback-color: var(--color-text);
16
+ --feedback-contrast: var(--color-text-contrast);
17
+ --feedback-surface: color-mix(in srgb, var(--feedback-color) 12%, var(--color-background));
18
+ --feedback-border: color-mix(in srgb, var(--feedback-color) 42%, var(--color-border));
19
+
20
+ @apply flex rounded-(--radius-surface) bg-(--feedback-surface) p-4 leading-5 text-(--feedback-color);
21
+ border: var(--border-width) solid var(--feedback-border);
22
+
23
+ &.primary {
24
+ --feedback-color: var(--color-primary);
25
+ --feedback-contrast: var(--color-primary-contrast);
26
+ }
27
+
28
+ &.secondary {
29
+ --feedback-color: var(--color-accent);
30
+ --feedback-contrast: var(--color-accent-contrast);
31
+ }
32
+
33
+ &.success {
34
+ --feedback-color: var(--color-success);
35
+ --feedback-contrast: var(--color-success-contrast);
36
+ }
37
+
38
+ &.warning {
39
+ --feedback-color: var(--color-warning);
40
+ --feedback-contrast: var(--color-warning-contrast);
41
+ }
42
+
43
+ &.destructive,
44
+ &.danger,
45
+ &.error {
46
+ --feedback-color: var(--color-destructive);
47
+ --feedback-contrast: var(--color-destructive-contrast);
48
+ }
49
+
50
+ &.info {
51
+ --feedback-color: var(--color-info);
52
+ --feedback-contrast: var(--color-info-contrast);
53
+ }
54
+
55
+ &.flat {
56
+ @apply bg-(--feedback-color) text-(--feedback-contrast);
57
+ border-color: var(--feedback-color);
58
+ }
59
+
60
+ &.soft {
61
+ @apply border-transparent bg-(--feedback-surface) text-(--feedback-color);
62
+ }
63
+
64
+ &.left-accent {
65
+ @apply border-y-transparent border-r-transparent bg-(--feedback-surface) text-(--feedback-color);
66
+ border-left-width: 4px;
67
+ border-left-color: var(--feedback-color);
68
+ }
69
+
70
+ &.icon {
71
+ @apply relative pl-11;
72
+
73
+ &::before {
74
+ @apply absolute top-4 left-4 block size-5 bg-current;
75
+
76
+ content: "";
77
+ mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" 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>');
78
+ -webkit-mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" 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>');
79
+ mask-size: contain;
80
+ -webkit-mask-size: contain;
81
+ mask-repeat: no-repeat;
82
+ -webkit-mask-repeat: no-repeat;
83
+ mask-position: center;
84
+ -webkit-mask-position: center;
85
+ }
86
+
87
+ &.success::before {
88
+ mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" 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="m9 12 2 2 4-4"/></svg>');
89
+ -webkit-mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" 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="m9 12 2 2 4-4"/></svg>');
90
+ }
91
+
92
+ &.warning::before {
93
+ mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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>');
94
+ -webkit-mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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>');
95
+ }
96
+
97
+ &.destructive::before,
98
+ &.danger::before,
99
+ &.error::before {
100
+ mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" 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="m15 9-6 6"/><path d="m9 9 6 6"/></svg>');
101
+ -webkit-mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" 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="m15 9-6 6"/><path d="m9 9 6 6"/></svg>');
102
+ }
103
+
104
+ &.info::before {
105
+ mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" 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>');
106
+ -webkit-mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" 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>');
107
+ }
108
+ }
109
+ }
110
+
111
+ @utility badge {
112
+ --feedback-color: var(--color-text);
113
+ --feedback-contrast: var(--color-text-contrast);
114
+ --feedback-surface: color-mix(in srgb, var(--feedback-color) 12%, var(--color-background));
115
+ --feedback-border: color-mix(in srgb, var(--feedback-color) 42%, var(--color-border));
116
+
117
+ @apply inline-flex min-h-6 w-fit items-center gap-1 rounded-full bg-(--feedback-surface) px-2.5 text-xs font-bold text-(--feedback-color);
118
+ border: var(--border-width) solid var(--feedback-border);
119
+
120
+ &.primary {
121
+ --feedback-color: var(--color-primary);
122
+ --feedback-contrast: var(--color-primary-contrast);
123
+ }
124
+
125
+ &.secondary {
126
+ --feedback-color: var(--color-accent);
127
+ --feedback-contrast: var(--color-accent-contrast);
128
+ }
129
+
130
+ &.success {
131
+ --feedback-color: var(--color-success);
132
+ --feedback-contrast: var(--color-success-contrast);
133
+ }
134
+
135
+ &.warning {
136
+ --feedback-color: var(--color-warning);
137
+ --feedback-contrast: var(--color-warning-contrast);
138
+ }
139
+
140
+ &.destructive,
141
+ &.danger,
142
+ &.error {
143
+ --feedback-color: var(--color-destructive);
144
+ --feedback-contrast: var(--color-destructive-contrast);
145
+ }
146
+
147
+ &.info {
148
+ --feedback-color: var(--color-info);
149
+ --feedback-contrast: var(--color-info-contrast);
150
+ }
151
+
152
+ &.flat {
153
+ @apply bg-(--feedback-color) text-(--feedback-contrast);
154
+ border-color: var(--feedback-color);
155
+ }
156
+
157
+ &.soft {
158
+ @apply border-transparent;
159
+ }
160
+ }
161
+
162
+ @utility skeleton {
163
+ @apply min-h-4 w-full rounded-(--radius-control);
164
+
165
+ animation: anim-skeleton calc(var(--motion-duration-slow) * 4) linear infinite;
166
+ background: linear-gradient(90deg, var(--color-surface), var(--color-foreground), var(--color-surface));
167
+ background-size: 200% 100%;
168
+ }
169
+
170
+ @utility progress {
171
+ --progress-color: var(--color-text);
172
+ --progress-surface: color-mix(var(--color-text-subtle) 60%, transparent);
173
+
174
+ @apply h-2.5 w-full overflow-hidden rounded-full bg-(--progress-surface);
175
+
176
+ > * {
177
+ @apply block h-full transition-[width] duration-(--motion-duration-slow) ease-(--motion-ease);
178
+
179
+ background-color: var(--progress-color);
180
+ width: var(--progress-value, 0%);
181
+ }
182
+
183
+ &.primary {
184
+ --progress-color: var(--color-primary);
185
+ --progress-surface: color-mix(var(--color-primary-subtle) 60%, transparent);
186
+ }
187
+
188
+ &.secondary {
189
+ --progress-color: var(--color-accent);
190
+ --progress-surface: color-mix(var(--color-accent-subtle) 60%, transparent);
191
+ }
192
+
193
+ &.success {
194
+ --progress-color: var(--color-success);
195
+ --progress-surface: color-mix(var(--color-success-subtle) 60%, transparent);
196
+ }
197
+
198
+ &.warning {
199
+ --progress-color: var(--color-warning);
200
+ --progress-surface: color-mix(var(--color-warning-subtle) 60%, transparent);
201
+ }
202
+
203
+ &.destructive,
204
+ &.danger,
205
+ &.error {
206
+ --progress-color: var(--color-destructive);
207
+ --progress-surface: color-mix(var(--color-destructive-subtle) 60%, transparent);
208
+ }
209
+
210
+ &.info {
211
+ --progress-color: var(--color-info);
212
+ --progress-surface: color-mix(var(--color-info-subtle) 60%, transparent);
213
+ }
214
+ }