@devslab/dds-css 0.25.1 → 0.26.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/README.ko.md +3 -0
- package/README.md +3 -0
- package/dist/components/button.css +49 -3
- package/dist/components/iconbutton.css +9 -0
- package/dist/components/spinner.css +15 -1
- package/dist/dds.css +73 -4
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -20,6 +20,9 @@ EmptyState** (BottomSheet은 Dialog의 네이티브 짝 — Phase 3).
|
|
|
20
20
|
- 다크 모드는 공짜: `<html>` 또는 임의 서브트리에 `data-theme="dark"`
|
|
21
21
|
(`data-theme="light"`로 되고정). 어떤 컴포넌트에도 다크 분기가 없습니다.
|
|
22
22
|
- 개별 임포트: `@devslab/dds-css/components/button.css` 등.
|
|
23
|
+
- 진행 중인 버튼(`aria-busy="true"` + `.dds-spinner` 자식)은 크기와 이름을
|
|
24
|
+
그대로 유지합니다. 라벨은 지우지 않고 숨기고, 스피너를 그 위에 겹쳐
|
|
25
|
+
그립니다(D-043).
|
|
23
26
|
- 컴포넌트별 마크업 계약·상태·접근성 체크리스트·do/don't:
|
|
24
27
|
[`docs/components.ko.md`](../../docs/components.ko.md).
|
|
25
28
|
라이브 레퍼런스: `preview/components.html` (먼저 `pnpm build`).
|
package/README.md
CHANGED
|
@@ -21,6 +21,9 @@ the native counterpart of Dialog — Phase 3).
|
|
|
21
21
|
- Dark mode is free: `data-theme="dark"` on `<html>` or any subtree
|
|
22
22
|
(`data-theme="light"` pins back). No component has a dark branch.
|
|
23
23
|
- Per-component imports: `@devslab/dds-css/components/button.css` etc.
|
|
24
|
+
- A loading button (`aria-busy="true"` + a `.dds-spinner` child) keeps its
|
|
25
|
+
size and name: the label is hidden, not removed, and the spinner is drawn
|
|
26
|
+
over it (D-043).
|
|
24
27
|
- Markup contracts, states, accessibility checklists and do/don't per
|
|
25
28
|
component: [`docs/components.md`](../../docs/components.md).
|
|
26
29
|
Live reference: `preview/components.html` (run `pnpm build` first).
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Variants: primary / secondary / ghost / danger. Sizes: sm / md(default) / lg.
|
|
3
3
|
* Control heights come from the spacing scale (32/40/48); type from the typo
|
|
4
4
|
* scale (label for sm/md, body-1 size for lg). Loading = aria-busy="true" with
|
|
5
|
-
* a .dds-spinner child. */
|
|
5
|
+
* a .dds-spinner child; the button keeps its size while loading (D-043). */
|
|
6
6
|
|
|
7
7
|
.dds-btn {
|
|
8
8
|
appearance: none;
|
|
@@ -29,6 +29,10 @@
|
|
|
29
29
|
-webkit-tap-highlight-color: transparent;
|
|
30
30
|
white-space: normal;
|
|
31
31
|
overflow-wrap: anywhere;
|
|
32
|
+
/* Containing block for the loading spinner, which is centred over the
|
|
33
|
+
* label rather than placed beside it. Set here, not only while loading,
|
|
34
|
+
* so a consumer's own position (a fixed action button) still wins. */
|
|
35
|
+
position: relative;
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
.dds-btn:focus-visible {
|
|
@@ -37,13 +41,36 @@
|
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
/* opacity is allowed for disabled only (spec §4.2) */
|
|
40
|
-
.dds-btn[disabled]
|
|
41
|
-
.dds-btn[aria-busy="true"] {
|
|
44
|
+
.dds-btn[disabled] {
|
|
42
45
|
opacity: 0.45;
|
|
43
46
|
cursor: not-allowed;
|
|
44
47
|
}
|
|
48
|
+
|
|
49
|
+
/* Loading (D-043). The button keeps the exact box its label gives it, so the
|
|
50
|
+
* controls beside it do not move and a wrapped label does not wrap again
|
|
51
|
+
* when progress starts. The label stays in the layout and in the
|
|
52
|
+
* accessibility tree (its accessible name does not change) but is not
|
|
53
|
+
* painted: the text fill is transparent and element children (icons) are
|
|
54
|
+
* fully transparent. The spinner is taken out of the flow and centred over
|
|
55
|
+
* the label; it keeps the button's text color (currentColor).
|
|
56
|
+
*
|
|
57
|
+
* Not dimmed: the spinner is the only thing painted, and at the disabled
|
|
58
|
+
* 0.45 opacity it drops to about 2:1 against the button (WCAG 1.4.11 asks
|
|
59
|
+
* 3:1). The framework Buttons also set [disabled], so this rule comes after
|
|
60
|
+
* the one above to put the opacity back. */
|
|
45
61
|
.dds-btn[aria-busy="true"] {
|
|
62
|
+
opacity: 1;
|
|
63
|
+
cursor: not-allowed;
|
|
46
64
|
pointer-events: none;
|
|
65
|
+
-webkit-text-fill-color: transparent;
|
|
66
|
+
}
|
|
67
|
+
.dds-btn[aria-busy="true"] > :not(.dds-spinner) {
|
|
68
|
+
opacity: 0;
|
|
69
|
+
}
|
|
70
|
+
.dds-btn[aria-busy="true"] > .dds-spinner {
|
|
71
|
+
position: absolute;
|
|
72
|
+
inset: 0;
|
|
73
|
+
margin: auto;
|
|
47
74
|
}
|
|
48
75
|
|
|
49
76
|
/* variants ---------------------------------------------------------------- */
|
|
@@ -115,3 +142,22 @@
|
|
|
115
142
|
min-block-size: 44px;
|
|
116
143
|
}
|
|
117
144
|
}
|
|
145
|
+
|
|
146
|
+
/* Forced colors repaint every text color, transparent included, so the
|
|
147
|
+
* hidden label would come back on top of the spinner. A loading button opts
|
|
148
|
+
* out and paints itself with system colors: the face of a button, the
|
|
149
|
+
* disabled border, and the spinner in button text. No transition: opted out,
|
|
150
|
+
* the background would fade from the brand color it no longer forces. */
|
|
151
|
+
@media (forced-colors: active) {
|
|
152
|
+
.dds-btn[aria-busy="true"] {
|
|
153
|
+
forced-color-adjust: none;
|
|
154
|
+
background-color: ButtonFace;
|
|
155
|
+
border-color: GrayText;
|
|
156
|
+
color: ButtonText;
|
|
157
|
+
transition: none;
|
|
158
|
+
}
|
|
159
|
+
.dds-btn[aria-busy="true"] > .dds-spinner {
|
|
160
|
+
forced-color-adjust: none;
|
|
161
|
+
color: ButtonText;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -38,6 +38,15 @@
|
|
|
38
38
|
cursor: not-allowed;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/* Loading: the framework IconButtons swap the icon for a .dds-spinner, which
|
|
42
|
+
* the fixed box already centres. Same rule as .dds-btn (D-043): not dimmed,
|
|
43
|
+
* because a 0.45 spinner falls below 3:1 (danger is about 2.3:1). */
|
|
44
|
+
.dds-iconbtn[aria-busy="true"] {
|
|
45
|
+
opacity: 1;
|
|
46
|
+
cursor: not-allowed;
|
|
47
|
+
pointer-events: none;
|
|
48
|
+
}
|
|
49
|
+
|
|
41
50
|
/* variants ---------------------------------------------------------------- */
|
|
42
51
|
|
|
43
52
|
.dds-iconbtn--secondary {
|
|
@@ -27,8 +27,22 @@
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/* base.css stops every dds- animation after one 0.01ms run under reduced
|
|
31
|
+
* motion. The :root prefix ties its specificity and this file loads after
|
|
32
|
+
* base.css, so the spinner keeps turning, at half speed, instead of freezing. */
|
|
30
33
|
@media (prefers-reduced-motion: reduce) {
|
|
31
|
-
.dds-spinner {
|
|
34
|
+
:root .dds-spinner {
|
|
32
35
|
animation-duration: 1600ms;
|
|
36
|
+
animation-iteration-count: infinite;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Forced colors repaint the transparent top border too, which closes the gap
|
|
41
|
+
* and leaves a ring that looks still. Opt out and paint it in the system text
|
|
42
|
+
* color (a loading button sets ButtonText instead, button.css). */
|
|
43
|
+
@media (forced-colors: active) {
|
|
44
|
+
.dds-spinner {
|
|
45
|
+
forced-color-adjust: none;
|
|
46
|
+
color: CanvasText;
|
|
33
47
|
}
|
|
34
48
|
}
|
package/dist/dds.css
CHANGED
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
* Variants: primary / secondary / ghost / danger. Sizes: sm / md(default) / lg.
|
|
53
53
|
* Control heights come from the spacing scale (32/40/48); type from the typo
|
|
54
54
|
* scale (label for sm/md, body-1 size for lg). Loading = aria-busy="true" with
|
|
55
|
-
* a .dds-spinner child. */
|
|
55
|
+
* a .dds-spinner child; the button keeps its size while loading (D-043). */
|
|
56
56
|
|
|
57
57
|
.dds-btn {
|
|
58
58
|
appearance: none;
|
|
@@ -79,6 +79,10 @@
|
|
|
79
79
|
-webkit-tap-highlight-color: transparent;
|
|
80
80
|
white-space: normal;
|
|
81
81
|
overflow-wrap: anywhere;
|
|
82
|
+
/* Containing block for the loading spinner, which is centred over the
|
|
83
|
+
* label rather than placed beside it. Set here, not only while loading,
|
|
84
|
+
* so a consumer's own position (a fixed action button) still wins. */
|
|
85
|
+
position: relative;
|
|
82
86
|
}
|
|
83
87
|
|
|
84
88
|
.dds-btn:focus-visible {
|
|
@@ -87,13 +91,36 @@
|
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
/* opacity is allowed for disabled only (spec §4.2) */
|
|
90
|
-
.dds-btn[disabled]
|
|
91
|
-
.dds-btn[aria-busy="true"] {
|
|
94
|
+
.dds-btn[disabled] {
|
|
92
95
|
opacity: 0.45;
|
|
93
96
|
cursor: not-allowed;
|
|
94
97
|
}
|
|
98
|
+
|
|
99
|
+
/* Loading (D-043). The button keeps the exact box its label gives it, so the
|
|
100
|
+
* controls beside it do not move and a wrapped label does not wrap again
|
|
101
|
+
* when progress starts. The label stays in the layout and in the
|
|
102
|
+
* accessibility tree (its accessible name does not change) but is not
|
|
103
|
+
* painted: the text fill is transparent and element children (icons) are
|
|
104
|
+
* fully transparent. The spinner is taken out of the flow and centred over
|
|
105
|
+
* the label; it keeps the button's text color (currentColor).
|
|
106
|
+
*
|
|
107
|
+
* Not dimmed: the spinner is the only thing painted, and at the disabled
|
|
108
|
+
* 0.45 opacity it drops to about 2:1 against the button (WCAG 1.4.11 asks
|
|
109
|
+
* 3:1). The framework Buttons also set [disabled], so this rule comes after
|
|
110
|
+
* the one above to put the opacity back. */
|
|
95
111
|
.dds-btn[aria-busy="true"] {
|
|
112
|
+
opacity: 1;
|
|
113
|
+
cursor: not-allowed;
|
|
96
114
|
pointer-events: none;
|
|
115
|
+
-webkit-text-fill-color: transparent;
|
|
116
|
+
}
|
|
117
|
+
.dds-btn[aria-busy="true"] > :not(.dds-spinner) {
|
|
118
|
+
opacity: 0;
|
|
119
|
+
}
|
|
120
|
+
.dds-btn[aria-busy="true"] > .dds-spinner {
|
|
121
|
+
position: absolute;
|
|
122
|
+
inset: 0;
|
|
123
|
+
margin: auto;
|
|
97
124
|
}
|
|
98
125
|
|
|
99
126
|
/* variants ---------------------------------------------------------------- */
|
|
@@ -166,6 +193,25 @@
|
|
|
166
193
|
}
|
|
167
194
|
}
|
|
168
195
|
|
|
196
|
+
/* Forced colors repaint every text color, transparent included, so the
|
|
197
|
+
* hidden label would come back on top of the spinner. A loading button opts
|
|
198
|
+
* out and paints itself with system colors: the face of a button, the
|
|
199
|
+
* disabled border, and the spinner in button text. No transition: opted out,
|
|
200
|
+
* the background would fade from the brand color it no longer forces. */
|
|
201
|
+
@media (forced-colors: active) {
|
|
202
|
+
.dds-btn[aria-busy="true"] {
|
|
203
|
+
forced-color-adjust: none;
|
|
204
|
+
background-color: ButtonFace;
|
|
205
|
+
border-color: GrayText;
|
|
206
|
+
color: ButtonText;
|
|
207
|
+
transition: none;
|
|
208
|
+
}
|
|
209
|
+
.dds-btn[aria-busy="true"] > .dds-spinner {
|
|
210
|
+
forced-color-adjust: none;
|
|
211
|
+
color: ButtonText;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
169
215
|
/* IconButton — a Button whose only content is an icon (spec §4.3 v1 inventory).
|
|
170
216
|
* Same state set and sizes as .dds-btn (32/40/48 from the spacing scale) so an
|
|
171
217
|
* icon button never lands a few px off the text button next to it — the row
|
|
@@ -206,6 +252,15 @@
|
|
|
206
252
|
cursor: not-allowed;
|
|
207
253
|
}
|
|
208
254
|
|
|
255
|
+
/* Loading: the framework IconButtons swap the icon for a .dds-spinner, which
|
|
256
|
+
* the fixed box already centres. Same rule as .dds-btn (D-043): not dimmed,
|
|
257
|
+
* because a 0.45 spinner falls below 3:1 (danger is about 2.3:1). */
|
|
258
|
+
.dds-iconbtn[aria-busy="true"] {
|
|
259
|
+
opacity: 1;
|
|
260
|
+
cursor: not-allowed;
|
|
261
|
+
pointer-events: none;
|
|
262
|
+
}
|
|
263
|
+
|
|
209
264
|
/* variants ---------------------------------------------------------------- */
|
|
210
265
|
|
|
211
266
|
.dds-iconbtn--secondary {
|
|
@@ -1129,9 +1184,23 @@
|
|
|
1129
1184
|
}
|
|
1130
1185
|
}
|
|
1131
1186
|
|
|
1187
|
+
/* base.css stops every dds- animation after one 0.01ms run under reduced
|
|
1188
|
+
* motion. The :root prefix ties its specificity and this file loads after
|
|
1189
|
+
* base.css, so the spinner keeps turning, at half speed, instead of freezing. */
|
|
1132
1190
|
@media (prefers-reduced-motion: reduce) {
|
|
1133
|
-
.dds-spinner {
|
|
1191
|
+
:root .dds-spinner {
|
|
1134
1192
|
animation-duration: 1600ms;
|
|
1193
|
+
animation-iteration-count: infinite;
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
/* Forced colors repaint the transparent top border too, which closes the gap
|
|
1198
|
+
* and leaves a ring that looks still. Opt out and paint it in the system text
|
|
1199
|
+
* color (a loading button sets ButtonText instead, button.css). */
|
|
1200
|
+
@media (forced-colors: active) {
|
|
1201
|
+
.dds-spinner {
|
|
1202
|
+
forced-color-adjust: none;
|
|
1203
|
+
color: CanvasText;
|
|
1135
1204
|
}
|
|
1136
1205
|
}
|
|
1137
1206
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devslab/dds-css",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "DDS CSS component layer — class-based, framework-neutral, semantic-token-only (spec §4.3 v1 inventory, 20 components)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|