@codenhub/styles 0.0.2 → 0.0.4

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,56 @@
1
+ @import "tailwindcss";
2
+
3
+ @utility ai {
4
+ --ai-image: url("data:image/svg+xml;charset=utf-8,%3Csvg stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_V8m1%7Btransform-origin:center;animation:spinner_zKoa 2s linear infinite%7D.spinner_V8m1 circle%7Bstroke-linecap:round;animation:spinner_YpZS 1.5s ease-in-out infinite%7D@keyframes spinner_zKoa%7B100%25%7Btransform:rotate(360deg)%7D%7D@keyframes spinner_YpZS%7B0%25%7Bstroke-dasharray:0 150;stroke-dashoffset:0%7D47.5%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-16%7D95%25,100%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-59%7D%7D%3C/style%3E%3Cg class='spinner_V8m1'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'%3E%3C/circle%3E%3C/g%3E%3C/svg%3E");
5
+
6
+ mask-image: var(--ai-image);
7
+ -webkit-mask-image: var(--ai-image);
8
+ mask-size: contain;
9
+ -webkit-mask-size: contain;
10
+ mask-repeat: no-repeat;
11
+ -webkit-mask-repeat: no-repeat;
12
+ mask-position: center;
13
+ -webkit-mask-position: center;
14
+ }
15
+
16
+ @utility dots-wave {
17
+ --ai-image: url("data:image/svg+xml;charset=utf-8,%3Csvg fill='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E@keyframes spinner_8HQG%7B0%25,57.14%25%7Banimation-timing-function:cubic-bezier(.33,.66,.66,1);transform:translate(0)%7D28.57%25%7Banimation-timing-function:cubic-bezier(.33,0,.66,.33);transform:translateY(-6px)%7Dto%7Btransform:translate(0)%7D%7D.spinner_qM83%7Banimation:spinner_8HQG 1.05s infinite%7D%3C/style%3E%3Ccircle class='spinner_qM83' cx='4' cy='12' r='3'/%3E%3Ccircle class='spinner_qM83' cx='12' cy='12' r='3' style='animation-delay:.1s'/%3E%3Ccircle class='spinner_qM83' cx='20' cy='12' r='3' style='animation-delay:.2s'/%3E%3C/svg%3E");
18
+ }
19
+
20
+ @utility dots-fade {
21
+ --ai-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.dot%7Banimation:dot-anim 1s ease-in-out infinite;fill:currentColor%7D.d1%7Banimation-delay:-0.32s%7D.d2%7Banimation-delay:-0.16s%7D@keyframes dot-anim%7B0%25,80%25,100%25%7Btransform:scale(0.6);opacity:0.2%7D40%25%7Btransform:scale(1);opacity:1%7D%7D%3C/style%3E%3Ccircle class='dot d1' cx='4' cy='12' r='3' transform-origin='4px 12px'%3E%3C/circle%3E%3Ccircle class='dot d2' cx='12' cy='12' r='3' transform-origin='12px 12px'%3E%3C/circle%3E%3Ccircle class='dot d3' cx='20' cy='12' r='3' transform-origin='20px 12px'%3E%3C/circle%3E%3C/svg%3E");
22
+ }
23
+
24
+ @utility dots-queue {
25
+ --ai-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E@keyframes spinner_qtyZ%7B0%25%7Br:0%7D25%25%7Br:3px;cx:4px%7D50%25%7Br:3px;cx:12px%7D75%25%7Br:3px;cx:20px%7Dto%7Br:0;cx:20px%7D%7D.spinner_nOfF%7Banimation:spinner_qtyZ 2s cubic-bezier(.36,.6,.31,1) infinite%7D%3C/style%3E%3Ccircle class='spinner_nOfF' cx='4' cy='12' r='3'/%3E%3Ccircle class='spinner_nOfF' cx='4' cy='12' r='3' style='animation-delay:-.5s'/%3E%3Ccircle class='spinner_nOfF' cx='4' cy='12' r='3' style='animation-delay:-1s'/%3E%3Ccircle class='spinner_nOfF' cx='4' cy='12' r='3' style='animation-delay:-1.5s'/%3E%3C/svg%3E");
26
+ }
27
+
28
+ @utility dots-rotate {
29
+ --ai-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E@keyframes spinner_XVY9%7B50%25%7Btransform:rotate(180deg)%7Dto%25%7Btransform:rotate(360deg)%7D%7D%3C/style%3E%3Ccircle cx='12' cy='12' r='3'/%3E%3Cg style='transform-origin:center;animation:spinner_XVY9 1s cubic-bezier(.36,.6,.31,1) infinite'%3E%3Ccircle cx='4' cy='12' r='3'/%3E%3Ccircle cx='20' cy='12' r='3'/%3E%3C/g%3E%3C/svg%3E");
30
+ }
31
+
32
+ @utility dots-grow {
33
+ --ai-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E@keyframes spinner_xe7Q%7B93.75%25,to%7Br:3px%7D46.875%25%7Br:.2px%7D%7D.spinner_b2T7{animation:spinner_xe7Q .8s linear infinite}%3C/style%3E%3Ccircle class='spinner_b2T7' cx='4' cy='12' r='3'/%3E%3Ccircle class='spinner_b2T7' cx='12' cy='12' r='3' style='animation-delay:-.65s'/%3E%3Ccircle class='spinner_b2T7' cx='20' cy='12' r='3' style='animation-delay:-.5s'/%3E%3C/svg%3E");
34
+ }
35
+
36
+ @utility dots-grow-alternate {
37
+ --ai-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E@keyframes spinner_qhi1%7B0%25,to%7Br:1.5px%7D50%25%7Br:3px%7D%7D.spinner_I8Q1%7Banimation:spinner_qhi1 .75s linear infinite%7D%3C/style%3E%3Ccircle class='spinner_I8Q1' cx='4' cy='12' r='1.5'/%3E%3Ccircle class='spinner_I8Q1' cx='12' cy='12' r='3' style='animation-delay:-.375s'/%3E%3Ccircle class='spinner_I8Q1' cx='20' cy='12' r='1.5'/%3E%3C/svg%3E");
38
+ }
39
+
40
+ @utility dot-bounce {
41
+ --ai-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E@keyframes spinner_YeBj%7B0%25%7Banimation-timing-function:cubic-bezier(.33,0,.66,.33);cy:5px%7D46.875%25%7Bcy:20px;rx:4px;ry:4px%7D50%25%7Banimation-timing-function:cubic-bezier(.33,.66,.66,1);cy:20.5px;rx:4.8px;ry:3px%7D53.125%25%7Brx:4px;ry:4px%7Dto%7Bcy:5px%7D%7D%3C/style%3E%3Ccircle cx='12' cy='5' style='animation:spinner_YeBj .8s infinite' r='4'/%3E%3C/svg%3E");
42
+ }
43
+
44
+ @utility bars-wave {
45
+ --ai-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.bar%7Banimation:bar-anim 1s ease-in-out infinite;fill:currentColor%7D.b1%7Banimation-delay:-0.4s%7D.b2%7Banimation-delay:-0.2s%7D@keyframes bar-anim%7B0%25,100%25%7Btransform:scaleY(0.3)%7D50%25%7Btransform:scaleY(1)%7D%7D%3C/style%3E%3Crect class='bar b1' x='3' y='2' width='4' height='20' rx='2' transform-origin='5px 12px'%3E%3C/rect%3E%3Crect class='bar b2' x='10' y='2' width='4' height='20' rx='2' transform-origin='12px 12px'%3E%3C/rect%3E%3Crect class='bar b3' x='17' y='2' width='4' height='20' rx='2' transform-origin='19px 12px'%3E%3C/rect%3E%3C/svg%3E");
46
+ }
47
+
48
+ @utility pulse-ring {
49
+ --ai-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.pulse%7Banimation:pulse-anim 1.2s cubic-bezier(0, 0.2, 0.8, 1) infinite;stroke:currentColor;transform-origin:center%7D.p2%7Banimation-delay:-0.6s%7D@keyframes pulse-anim%7B0%25%7Btransform:scale(0);opacity:1%7D100%25%7Btransform:scale(1);opacity:0%7D%7D%3C/style%3E%3Ccircle class='pulse p1' cx='12' cy='12' r='10' fill='none' stroke-width='3'%3E%3C/circle%3E%3Ccircle class='pulse p2' cx='12' cy='12' r='10' fill='none' stroke-width='3'%3E%3C/circle%3E%3C/svg%3E");
50
+ }
51
+
52
+ @utility loader {
53
+ @apply ai bg-text inline-block size-8 [&.lg]:size-10 [&.sm]:size-6;
54
+
55
+ content: "";
56
+ }
@@ -0,0 +1,6 @@
1
+ @import "tailwindcss";
2
+ @import "../theme.css";
3
+
4
+ @utility empty-state {
5
+ @apply text-text-secondary flex flex-col items-center gap-3 p-[clamp(2rem,8vw,5rem)] text-center;
6
+ }
@@ -0,0 +1,60 @@
1
+ @import "tailwindcss";
2
+ @import "../theme.css";
3
+
4
+ @utility tooltip {
5
+ --tooltip-bg: var(--color-surface);
6
+ --tooltip-fg: var(--color-text);
7
+ --tooltip-gap: 0.5rem;
8
+ --tooltip-top: calc(var(--tooltip-gap) * -1);
9
+ --tooltip-right: auto;
10
+ --tooltip-bottom: auto;
11
+ --tooltip-left: 50%;
12
+ --tooltip-origin: bottom;
13
+ --tooltip-transform: translate(-50%, -100%);
14
+
15
+ @apply relative;
16
+
17
+ &.tooltip-icon {
18
+ @apply bg-surface text-text inline-flex size-5 cursor-default items-center justify-center rounded-full text-sm font-bold;
19
+ }
20
+
21
+ &[data-tooltip-position="bottom"] {
22
+ --tooltip-top: auto;
23
+ --tooltip-bottom: calc(var(--tooltip-gap) * -1);
24
+ --tooltip-origin: top;
25
+ --tooltip-transform: translate(-50%, 100%);
26
+ }
27
+
28
+ &[data-tooltip-position="left"] {
29
+ --tooltip-top: 50%;
30
+ --tooltip-left: calc(var(--tooltip-gap) * -1);
31
+ --tooltip-origin: right;
32
+ --tooltip-transform: translate(-100%, -50%);
33
+ }
34
+
35
+ &[data-tooltip-position="right"] {
36
+ --tooltip-top: 50%;
37
+ --tooltip-left: auto;
38
+ --tooltip-right: calc(var(--tooltip-gap) * -1);
39
+ --tooltip-origin: left;
40
+ --tooltip-transform: translate(100%, -50%);
41
+ }
42
+
43
+ &::after {
44
+ @apply pointer-events-none absolute z-(--z-popover) w-max max-w-72 rounded-(--radius-control) bg-(--tooltip-bg) px-2.5 py-2 text-sm font-normal whitespace-normal text-(--tooltip-fg) opacity-0 shadow-(--elevation-high) transition-[opacity,transform] duration-(--motion-duration-normal) ease-(--motion-ease);
45
+ content: attr(data-tooltip);
46
+ left: var(--tooltip-left);
47
+ right: var(--tooltip-right);
48
+ top: var(--tooltip-top);
49
+ bottom: var(--tooltip-bottom);
50
+ transform: var(--tooltip-transform) scale(0);
51
+ transform-origin: var(--tooltip-origin);
52
+ }
53
+
54
+ &:hover::after,
55
+ &:focus-visible::after,
56
+ &[data-state="open"]::after {
57
+ opacity: 1;
58
+ transform: var(--tooltip-transform) scale(1);
59
+ }
60
+ }
package/src/index.css CHANGED
@@ -1,93 +1,7 @@
1
1
  @import "tailwindcss";
2
2
 
3
- @import "./components.css";
4
-
5
- @utility text-display {
6
- @apply text-6xl font-bold tracking-tight md:text-8xl;
7
- }
8
-
9
- @utility text-title-lg {
10
- @apply text-5xl font-bold tracking-tight md:text-6xl;
11
- }
12
-
13
- @utility text-title {
14
- @apply text-3xl font-bold tracking-tight;
15
- }
16
-
17
- @utility text-title-sm {
18
- @apply text-xl font-semibold tracking-tight;
19
- }
20
-
21
- @utility text-label-lg {
22
- @apply text-lg font-medium tracking-wide;
23
- }
24
-
25
- @utility text-label {
26
- @apply text-base font-medium tracking-wide;
27
- }
28
-
29
- @utility text-body {
30
- @apply text-sm font-normal tracking-wider;
31
- }
32
-
33
- @layer base {
34
- ::-webkit-scrollbar {
35
- width: calc(var(--spacing) * 4);
36
- }
37
-
38
- ::-webkit-scrollbar-thumb {
39
- background: var(--color-primary);
40
- border: var(--spacing) solid var(--color-background);
41
- border-radius: calc(var(--spacing) * 2);
42
- }
43
-
44
- html,
45
- body {
46
- @apply bg-background text-text font-default min-h-dvh overflow-x-hidden;
47
- }
48
-
49
- body {
50
- @apply flex flex-col;
51
- }
52
-
53
- button {
54
- @apply not-disabled:cursor-pointer;
55
- }
56
-
57
- img {
58
- @apply select-none;
59
- }
60
-
61
- ::selection {
62
- background: var(--color-primary);
63
- color: var(--color-primary-contrast);
64
- }
65
-
66
- .selection-contrast::selection,
67
- .selection-contrast *::selection {
68
- background: var(--color-primary-contrast);
69
- color: var(--color-primary);
70
- }
71
-
72
- :focus-visible {
73
- outline: var(--focus-ring-width) solid var(--focus-ring);
74
- outline-offset: var(--focus-ring-offset);
75
- }
76
-
77
- @media (prefers-reduced-motion: reduce) {
78
- *,
79
- *::before,
80
- *::after {
81
- animation-duration: 0.01ms !important;
82
- animation-iteration-count: 1 !important;
83
- scroll-behavior: auto !important;
84
- transition-duration: 0.01ms !important;
85
- }
86
- }
87
-
88
- p,
89
- li,
90
- a {
91
- @apply text-body;
92
- }
93
- }
3
+ @import "./components/index.css";
4
+ @import "./theme.css";
5
+ @import "./typography.css";
6
+ @import "./utilities.css";
7
+ @import "./overrides.css";
@@ -0,0 +1,76 @@
1
+ @import "tailwindcss";
2
+ @import "./theme.css";
3
+ @import "./typography.css";
4
+
5
+ @layer base {
6
+ ::-webkit-scrollbar {
7
+ width: calc(var(--spacing) * 4);
8
+ }
9
+
10
+ ::-webkit-scrollbar-thumb {
11
+ background: var(--color-primary);
12
+ border: var(--spacing) solid var(--color-background);
13
+ border-radius: calc(var(--spacing) * 2);
14
+ }
15
+
16
+ html,
17
+ body {
18
+ @apply bg-background text-text font-default min-h-dvh overflow-x-hidden;
19
+ }
20
+
21
+ body {
22
+ @apply flex flex-col;
23
+ }
24
+
25
+ button {
26
+ @apply not-disabled:cursor-pointer;
27
+ }
28
+
29
+ img {
30
+ @apply select-none;
31
+ }
32
+
33
+ ::selection {
34
+ background: var(--color-primary);
35
+ color: var(--color-primary-contrast);
36
+ }
37
+
38
+ :focus-visible {
39
+ outline: var(--focus-ring-width) solid var(--focus-ring);
40
+ outline-offset: var(--focus-ring-offset);
41
+ }
42
+
43
+ @media (prefers-reduced-motion: reduce) {
44
+ *,
45
+ *::before,
46
+ *::after {
47
+ animation-duration: 0.01ms !important;
48
+ animation-iteration-count: 1 !important;
49
+ scroll-behavior: auto !important;
50
+ transition-duration: 0.01ms !important;
51
+ }
52
+ }
53
+
54
+ p,
55
+ li,
56
+ a {
57
+ @apply text-body;
58
+ }
59
+ }
60
+
61
+ @layer components {
62
+ [aria-current="page"] {
63
+ @apply text-primary font-bold;
64
+ }
65
+
66
+ @media (forced-colors: active) {
67
+ .btn,
68
+ .ipt,
69
+ .textarea,
70
+ .select,
71
+ .alert,
72
+ .badge {
73
+ border-color: CanvasText;
74
+ }
75
+ }
76
+ }
package/src/theme.css CHANGED
@@ -8,10 +8,10 @@
8
8
  --color-primary-hover: var(--color-neutral-700);
9
9
  --color-primary-subtle: var(--color-neutral-200);
10
10
  --color-primary-strong: var(--color-neutral-900);
11
- --color-accent: var(--color-neutral-300);
12
- --color-accent-contrast: var(--color-neutral-950);
13
- --color-accent-hover: var(--color-neutral-400);
14
- --color-accent-subtle: var(--color-neutral-200);
11
+ --color-accent: var(--color-neutral-600);
12
+ --color-accent-contrast: var(--color-neutral-50);
13
+ --color-accent-hover: var(--color-neutral-700);
14
+ --color-accent-subtle: var(--color-neutral-100);
15
15
  --color-accent-strong: var(--color-neutral-800);
16
16
  --color-border: var(--color-neutral-300);
17
17
  --color-border-hover: var(--color-neutral-600);
@@ -20,6 +20,10 @@
20
20
  --color-surface: var(--color-neutral-200);
21
21
  --color-text: var(--color-neutral-950);
22
22
  --color-text-secondary: var(--color-neutral-700);
23
+ --color-text-contrast: var(--color-neutral-50);
24
+ --color-text-hover: var(--color-neutral-700);
25
+ --color-text-subtle: var(--color-neutral-200);
26
+ --color-text-strong: var(--color-neutral-900);
23
27
 
24
28
  --color-success: var(--color-emerald-600);
25
29
  --color-success-contrast: var(--color-neutral-50);
@@ -58,16 +62,15 @@
58
62
  --layout-cluster-gap: 0.75rem;
59
63
  --layout-grid-min: 16rem;
60
64
 
65
+ --radius-small: 0.25rem;
61
66
  --radius-control: 0.5rem;
62
67
  --radius-surface: 0.875rem;
63
- --radius-overlay: 0.75rem;
64
68
  --control-height: 2.5rem;
65
69
  --border-width: 1px;
66
70
 
67
71
  --elevation-low: 0 1px 2px rgb(15 23 42 / 0.06), 0 1px 3px rgb(15 23 42 / 0.08);
68
72
  --elevation-mid: 0 4px 6px -1px rgb(15 23 42 / 0.08), 0 2px 4px -2px rgb(15 23 42 / 0.08);
69
73
  --elevation-high: 0 20px 25px -5px rgb(15 23 42 / 0.1), 0 8px 10px -6px rgb(15 23 42 / 0.1);
70
- --elevation-overlay: 0 24px 48px rgb(15 23 42 / 0.18);
71
74
 
72
75
  --focus-ring: color-mix(in srgb, var(--color-primary) 64%, transparent);
73
76
  --focus-ring-offset: 2px;
@@ -78,9 +81,58 @@
78
81
  --motion-duration-slow: 400ms;
79
82
  --motion-ease: cubic-bezier(0.2, 0, 0, 1);
80
83
 
81
- --backdrop-overlay: blur(16px);
82
84
  --z-popover: 40;
83
- --z-toast: 60;
85
+
86
+ --surface-hover-transform: translateY(-1px);
87
+ }
88
+
89
+ .light,
90
+ .theme-light,
91
+ [data-theme="light"] {
92
+ color-scheme: light;
93
+
94
+ --color-primary: var(--color-neutral-950);
95
+ --color-primary-contrast: var(--color-neutral-50);
96
+ --color-primary-hover: var(--color-neutral-700);
97
+ --color-primary-subtle: var(--color-neutral-200);
98
+ --color-primary-strong: var(--color-neutral-900);
99
+ --color-accent: var(--color-neutral-600);
100
+ --color-accent-contrast: var(--color-neutral-50);
101
+ --color-accent-hover: var(--color-neutral-700);
102
+ --color-accent-subtle: var(--color-neutral-100);
103
+ --color-accent-strong: var(--color-neutral-800);
104
+ --color-border: var(--color-neutral-300);
105
+ --color-border-hover: var(--color-neutral-600);
106
+ --color-background: var(--color-neutral-50);
107
+ --color-foreground: var(--color-neutral-100);
108
+ --color-surface: var(--color-neutral-200);
109
+ --color-text: var(--color-neutral-950);
110
+ --color-text-secondary: var(--color-neutral-700);
111
+ --color-text-contrast: var(--color-neutral-50);
112
+ --color-text-hover: var(--color-neutral-700);
113
+ --color-text-subtle: var(--color-neutral-200);
114
+ --color-text-strong: var(--color-neutral-900);
115
+
116
+ --color-success: var(--color-emerald-600);
117
+ --color-success-contrast: var(--color-neutral-50);
118
+ --color-success-hover: color-mix(in srgb, var(--color-success) 82%, var(--color-success-strong));
119
+ --color-success-subtle: var(--color-emerald-100);
120
+ --color-success-strong: var(--color-emerald-800);
121
+ --color-warning: var(--color-amber-600);
122
+ --color-warning-contrast: var(--color-neutral-50);
123
+ --color-warning-hover: color-mix(in srgb, var(--color-warning) 82%, var(--color-warning-strong));
124
+ --color-warning-subtle: var(--color-amber-100);
125
+ --color-warning-strong: var(--color-amber-800);
126
+ --color-destructive: var(--color-rose-700);
127
+ --color-destructive-contrast: var(--color-neutral-50);
128
+ --color-destructive-hover: color-mix(in srgb, var(--color-destructive) 82%, var(--color-destructive-strong));
129
+ --color-destructive-subtle: var(--color-rose-100);
130
+ --color-destructive-strong: var(--color-rose-800);
131
+ --color-info: var(--color-indigo-600);
132
+ --color-info-contrast: var(--color-neutral-50);
133
+ --color-info-hover: color-mix(in srgb, var(--color-info) 82%, var(--color-info-strong));
134
+ --color-info-subtle: var(--color-indigo-100);
135
+ --color-info-strong: var(--color-indigo-800);
84
136
  }
85
137
 
86
138
  .dark {
@@ -91,9 +143,9 @@
91
143
  --color-primary-hover: var(--color-neutral-200);
92
144
  --color-primary-subtle: var(--color-neutral-900);
93
145
  --color-primary-strong: var(--color-neutral-100);
94
- --color-accent: var(--color-neutral-700);
95
- --color-accent-contrast: var(--color-neutral-50);
96
- --color-accent-hover: var(--color-neutral-500);
146
+ --color-accent: var(--color-neutral-400);
147
+ --color-accent-contrast: var(--color-neutral-950);
148
+ --color-accent-hover: var(--color-neutral-300);
97
149
  --color-accent-subtle: var(--color-neutral-800);
98
150
  --color-accent-strong: var(--color-neutral-200);
99
151
  --color-border: var(--color-neutral-700);
@@ -103,6 +155,10 @@
103
155
  --color-surface: var(--color-neutral-800);
104
156
  --color-text: var(--color-neutral-50);
105
157
  --color-text-secondary: var(--color-neutral-400);
158
+ --color-text-contrast: var(--color-neutral-950);
159
+ --color-text-hover: var(--color-neutral-200);
160
+ --color-text-subtle: var(--color-neutral-900);
161
+ --color-text-strong: var(--color-neutral-100);
106
162
 
107
163
  --color-success: var(--color-emerald-600);
108
164
  --color-success-contrast: var(--color-neutral-50);
@@ -126,4 +182,53 @@
126
182
  --color-info-strong: var(--color-indigo-100);
127
183
  }
128
184
 
185
+ @media (prefers-color-scheme: dark) {
186
+ :root:not(.light, .theme-light, [data-theme="light"]) {
187
+ color-scheme: dark;
188
+
189
+ --color-primary: var(--color-neutral-50);
190
+ --color-primary-contrast: var(--color-neutral-950);
191
+ --color-primary-hover: var(--color-neutral-200);
192
+ --color-primary-subtle: var(--color-neutral-900);
193
+ --color-primary-strong: var(--color-neutral-100);
194
+ --color-accent: var(--color-neutral-400);
195
+ --color-accent-contrast: var(--color-neutral-950);
196
+ --color-accent-hover: var(--color-neutral-300);
197
+ --color-accent-subtle: var(--color-neutral-800);
198
+ --color-accent-strong: var(--color-neutral-200);
199
+ --color-border: var(--color-neutral-700);
200
+ --color-border-hover: var(--color-neutral-500);
201
+ --color-background: var(--color-neutral-950);
202
+ --color-foreground: var(--color-neutral-900);
203
+ --color-surface: var(--color-neutral-800);
204
+ --color-text: var(--color-neutral-50);
205
+ --color-text-secondary: var(--color-neutral-400);
206
+ --color-text-contrast: var(--color-neutral-950);
207
+ --color-text-hover: var(--color-neutral-200);
208
+ --color-text-subtle: var(--color-neutral-900);
209
+ --color-text-strong: var(--color-neutral-100);
210
+
211
+ --color-success: var(--color-emerald-600);
212
+ --color-success-contrast: var(--color-neutral-50);
213
+ --color-success-hover: color-mix(in srgb, var(--color-success) 82%, var(--color-success-strong));
214
+ --color-success-subtle: var(--color-emerald-800);
215
+ --color-success-strong: var(--color-emerald-100);
216
+ --color-warning: var(--color-amber-600);
217
+ --color-warning-contrast: var(--color-neutral-50);
218
+ --color-warning-hover: color-mix(in srgb, var(--color-warning) 82%, var(--color-warning-strong));
219
+ --color-warning-subtle: var(--color-amber-800);
220
+ --color-warning-strong: var(--color-amber-100);
221
+ --color-destructive: var(--color-rose-700);
222
+ --color-destructive-contrast: var(--color-neutral-50);
223
+ --color-destructive-hover: color-mix(in srgb, var(--color-destructive) 82%, var(--color-destructive-strong));
224
+ --color-destructive-subtle: var(--color-rose-800);
225
+ --color-destructive-strong: var(--color-rose-100);
226
+ --color-info: var(--color-indigo-600);
227
+ --color-info-contrast: var(--color-neutral-50);
228
+ --color-info-hover: color-mix(in srgb, var(--color-info) 82%, var(--color-info-strong));
229
+ --color-info-subtle: var(--color-indigo-800);
230
+ --color-info-strong: var(--color-indigo-100);
231
+ }
232
+ }
233
+
129
234
  @custom-variant dark (&:where(.dark, .dark *));
@@ -0,0 +1,30 @@
1
+ @import "tailwindcss";
2
+ @import "./theme.css";
3
+
4
+ @utility text-display {
5
+ @apply text-6xl font-bold tracking-tight md:text-8xl;
6
+ }
7
+
8
+ @utility text-title-lg {
9
+ @apply text-5xl font-bold tracking-tight md:text-6xl;
10
+ }
11
+
12
+ @utility text-title {
13
+ @apply text-3xl font-bold tracking-tight;
14
+ }
15
+
16
+ @utility text-title-sm {
17
+ @apply text-xl font-semibold tracking-tight;
18
+ }
19
+
20
+ @utility text-label-lg {
21
+ @apply text-lg font-medium tracking-wide;
22
+ }
23
+
24
+ @utility text-label {
25
+ @apply text-base font-medium tracking-wide;
26
+ }
27
+
28
+ @utility text-body {
29
+ @apply text-sm font-normal tracking-wider;
30
+ }
@@ -0,0 +1,40 @@
1
+ @import "tailwindcss";
2
+ @import "./theme.css";
3
+
4
+ @utility stack {
5
+ @apply flex flex-col gap-(--layout-stack-gap);
6
+
7
+ &.tight {
8
+ --layout-stack-gap: 0.5rem;
9
+ }
10
+
11
+ &.loose {
12
+ --layout-stack-gap: 1.5rem;
13
+ }
14
+ }
15
+
16
+ @utility cluster {
17
+ @apply flex flex-wrap items-center gap-(--layout-cluster-gap);
18
+
19
+ &.between {
20
+ @apply justify-between;
21
+ }
22
+ }
23
+
24
+ @utility auto-grid {
25
+ @apply grid gap-(--layout-stack-gap);
26
+
27
+ grid-template-columns: repeat(auto-fit, minmax(min(var(--layout-grid-min), 100%), 1fr));
28
+ }
29
+
30
+ @utility selection-contrast {
31
+ &::selection,
32
+ & *::selection {
33
+ background: var(--color-primary-contrast);
34
+ color: var(--color-primary);
35
+ }
36
+ }
37
+
38
+ @utility code {
39
+ @apply bg-surface rounded-(--radius-small) px-1 py-0.5 font-mono;
40
+ }
@@ -1,53 +0,0 @@
1
- # @codenhub/styles Accessibility
2
-
3
- **Status:** IMPLEMENTED
4
- **Last updated:** 2026-06-12
5
- **Scope:** CSS accessibility behavior and non-goals for `@codenhub/styles`.
6
-
7
- This package provides CSS hooks for accessible states. It does not provide semantic HTML, ARIA attributes, keyboard behavior, focus management, validation, announcement timing, or JavaScript behavior.
8
-
9
- ## Provided By CSS
10
-
11
- | Feature | Behavior |
12
- | ------------------ | ------------------------------------------------------------------------------------------------------------------------- |
13
- | `:focus-visible` | Global focus-visible outline using `--focus-ring`, `--focus-ring-width`, and `--focus-ring-offset`. |
14
- | Form control focus | `.ipt`, `.textarea`, and `.select` use focused border and ring styles. |
15
- | Invalid controls | `[aria-invalid="true"]` applies destructive border/focus color on form controls. |
16
- | Disabled controls | `[disabled]`, `[aria-disabled="true"]`, `[data-disabled]`, and `.disabled` apply disabled cursor/opacity where supported. |
17
- | Current page | `[aria-current="page"]` applies primary color and stronger font weight. |
18
- | Open state | `[data-state="open"]` styles supported surfaces and tooltips. |
19
- | Reduced motion | `prefers-reduced-motion: reduce` globally shortens animations and transitions from the full stylesheet. |
20
- | Forced colors | `forced-colors: active` preserves visible borders on key helpers. |
21
-
22
- ## Required Outside CSS
23
-
24
- Use semantic HTML and behavior appropriate for the component.
25
-
26
- | UI | Required outside CSS |
27
- | ------------------ | --------------------------------------------------------------------------------------------------------------- |
28
- | Buttons | Use `<button>` for actions or accessible links for navigation. Add accessible names for `.btn.icon`. |
29
- | Forms | Use labels, `type`, validation logic, `aria-describedby`, and error message relationships. |
30
- | Alerts | Add `role="status"` or `role="alert"` based on announcement urgency. |
31
- | Toasts | Add live-region behavior, dismissal behavior, focus rules, and pause/timeout logic when needed. |
32
- | Tooltips | Provide accessible names/descriptions. CSS pseudo-element content is not enough for all assistive tech. |
33
- | Progress | Use semantic progress elements or ARIA values when numeric progress must be announced. |
34
- | Skeletons/spinners | Mark decorative loading visuals with `aria-hidden="true"` and expose loading state elsewhere when needed. |
35
- | Popovers/modals | Provide focus trapping, escape handling, inert background behavior, labels, and roles outside this CSS package. |
36
-
37
- ## State Attribute Guidance
38
-
39
- Prefer native attributes first:
40
-
41
- ```html
42
- <button class="btn primary" disabled>Saving</button>
43
- <input class="ipt" aria-invalid="true" aria-describedby="email-error" />
44
- ```
45
-
46
- Use ARIA or data attributes when native attributes are not available for the element or library:
47
-
48
- ```html
49
- <a class="btn secondary" aria-disabled="true">Unavailable</a>
50
- <button class="tooltip icon" data-state="open" data-tooltip="More details" aria-label="More details">?</button>
51
- ```
52
-
53
- `aria-disabled="true"` communicates disabled state but does not prevent activation. JavaScript or element choice must prevent activation when required.