@devslab/dds-css 0.3.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/LICENSE ADDED
@@ -0,0 +1,74 @@
1
+ DevsLab Source-Available License 1.0
2
+
3
+ Copyright (c) 2026 DevsLab. All rights reserved.
4
+
5
+ This license governs the DDS software, design tokens, components, icons,
6
+ documentation, and other materials distributed with this license (the
7
+ "Software").
8
+
9
+ 1. Permitted use
10
+
11
+ Subject to this license, DevsLab grants you a limited, worldwide,
12
+ non-exclusive, non-transferable, revocable, royalty-free right to:
13
+
14
+ - download and install unmodified copies of the Software;
15
+ - use and execute the Software for internal or commercial applications;
16
+ - compile, minify, tree-shake, and bundle unmodified Software solely as a
17
+ technical part of building an application; and
18
+ - distribute the resulting object code only as an inseparable part of that
19
+ application and not as a standalone library, design system, component kit,
20
+ template, or asset collection.
21
+
22
+ Normal application configuration through documented public APIs, design-token
23
+ overrides, CSS custom properties, and component properties is permitted and is
24
+ not considered modification of the Software.
25
+
26
+ 2. Restrictions
27
+
28
+ Except where applicable law does not allow the restriction, you may not:
29
+
30
+ - modify, adapt, translate, reverse engineer, or create derivative works of the
31
+ Software itself;
32
+ - publish, republish, sublicense, sell, rent, host, or redistribute the
33
+ Software or any modified version as a standalone package or downloadable
34
+ collection;
35
+ - use the Software or a substantial portion of it to create or operate a
36
+ competing general-purpose design system, UI kit, component library, icon
37
+ library, token library, template marketplace, or hosted equivalent;
38
+ - remove or alter copyright, license, attribution, or provenance notices; or
39
+ - use DevsLab names, logos, product marks, or trade dress except to make a
40
+ truthful statement that an application uses the Software.
41
+
42
+ No patent, trademark, design, or branding rights are granted except the
43
+ copyright permissions stated in Section 1.
44
+
45
+ 3. Ownership and feedback
46
+
47
+ The Software is licensed, not sold. DevsLab and its licensors retain all
48
+ right, title, and interest in the Software. You grant DevsLab a perpetual,
49
+ worldwide, irrevocable, royalty-free right to use feedback you voluntarily
50
+ provide about the Software.
51
+
52
+ 4. Termination
53
+
54
+ This license terminates automatically if you breach it. Upon termination, you
55
+ must stop using and distributing the Software and delete all copies under your
56
+ control. Sections 2, 3, 5, and 6 survive termination.
57
+
58
+ 5. Disclaimer and limitation of liability
59
+
60
+ THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
61
+ EXPRESS OR IMPLIED, INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR
62
+ PURPOSE, TITLE, AND NON-INFRINGEMENT. TO THE MAXIMUM EXTENT PERMITTED BY LAW,
63
+ DEVSLAB WILL NOT BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
64
+ CONSEQUENTIAL, EXEMPLARY, OR PUNITIVE DAMAGES, OR FOR LOSS OF DATA, PROFITS,
65
+ REVENUE, OR BUSINESS, ARISING FROM OR RELATED TO THE SOFTWARE.
66
+
67
+ 6. Governing law
68
+
69
+ This license is governed by the laws of the Republic of Korea, excluding
70
+ conflict-of-law rules. The Seoul Central District Court has exclusive
71
+ jurisdiction over disputes arising from this license.
72
+
73
+ This is a source-available license, not an open-source license approved by the
74
+ Open Source Initiative.
package/README.ko.md ADDED
@@ -0,0 +1,25 @@
1
+ # @devslab/dds-css
2
+
3
+ ๐ŸŒ [English](README.md)
4
+
5
+ DDS CSS ์ปดํฌ๋„ŒํŠธ ๋ ˆ์ด์–ด โ€” ํด๋ž˜์Šค ๊ธฐ๋ฐ˜, ํ”„๋ ˆ์ž„์›Œํฌ ์ค‘๋ฆฝ (์ŠคํŽ™ ยง2: ์ฝ”์–ด๋Š” CSS,
6
+ React/Vue/Ionic/SSR์€ ์†Œ๋น„์ž). ์ŠคํŽ™ ยง4.3 v1 ์ธ๋ฒคํ† ๋ฆฌ: **Button, IconButton,
7
+ TextField, Textarea, Select, Checkbox/Radio, Switch, Badge, Chip, Avatar,
8
+ Spinner, Skeleton, Divider, Card, ListRow, Tabs, Dialog, Toast, Tooltip,
9
+ EmptyState** (BottomSheet์€ Dialog์˜ ๋„ค์ดํ‹ฐ๋ธŒ ์ง โ€” Phase 3).
10
+
11
+ ```html
12
+ <link rel="stylesheet" href=".../dds-tokens/tokens.css"> <!-- ๋จผ์ € -->
13
+ <link rel="stylesheet" href=".../dds-css/dds.css">
14
+ <button class="dds-btn dds-btn--primary">์ €์žฅ</button>
15
+ ```
16
+
17
+ - ๋ชจ๋“  ๊ฐ’์ด ์‹œ๋งจํ‹ฑ ํ† ํฐ ์ฐธ์กฐ(`var(--dds-*)`) โ€” `scripts/check-css.mjs`๊ฐ€
18
+ `src/`์˜ hex/rgb/hsl/color-mix๋ฅผ ๊ธฐ๊ณ„์ ์œผ๋กœ ๊ธˆ์ง€ํ•˜๋ฏ€๋กœ, ํ•˜๋“œ์ฝ”๋”ฉ๋œ ์ƒ‰์€
19
+ ๋ฆฌ๋ทฐ๊ฐ€ ์•„๋‹ˆ๋ผ CI์—์„œ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.
20
+ - ๋‹คํฌ ๋ชจ๋“œ๋Š” ๊ณต์งœ: `<html>` ๋˜๋Š” ์ž„์˜ ์„œ๋ธŒํŠธ๋ฆฌ์— `data-theme="dark"`
21
+ (`data-theme="light"`๋กœ ๋˜๊ณ ์ •). ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ์—๋„ ๋‹คํฌ ๋ถ„๊ธฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
22
+ - ๊ฐœ๋ณ„ ์ž„ํฌํŠธ: `@devslab/dds-css/components/button.css` ๋“ฑ.
23
+ - ์ปดํฌ๋„ŒํŠธ๋ณ„ ๋งˆํฌ์—… ๊ณ„์•ฝยท์ƒํƒœยท์ ‘๊ทผ์„ฑ ์ฒดํฌ๋ฆฌ์ŠคํŠธยทdo/don't:
24
+ [`docs/components.ko.md`](../../docs/components.ko.md).
25
+ ๋ผ์ด๋ธŒ ๋ ˆํผ๋Ÿฐ์Šค: `preview/components.html` (๋จผ์ € `pnpm build`).
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # @devslab/dds-css
2
+
3
+ ๐ŸŒ [ํ•œ๊ตญ์–ด](README.ko.md)
4
+
5
+ DDS CSS component layer โ€” class-based and framework-neutral (spec ยง2: the
6
+ core is CSS; React/Vue/Ionic/SSR are consumers). The spec ยง4.3 v1
7
+ inventory: **Button, IconButton, TextField, Textarea, Select,
8
+ Checkbox/Radio, Switch, Badge, Chip, Avatar, Spinner, Skeleton, Divider,
9
+ Card, ListRow, Tabs, Dialog, Toast, Tooltip, EmptyState** (BottomSheet is
10
+ the native counterpart of Dialog โ€” Phase 3).
11
+
12
+ ```html
13
+ <link rel="stylesheet" href=".../dds-tokens/tokens.css"> <!-- first -->
14
+ <link rel="stylesheet" href=".../dds-css/dds.css">
15
+ <button class="dds-btn dds-btn--primary">Save</button>
16
+ ```
17
+
18
+ - Every value is a semantic token reference (`var(--dds-*)`) โ€”
19
+ `scripts/check-css.mjs` mechanically bans hex/rgb/hsl/color-mix in `src/`,
20
+ so a hardcoded color fails CI, not review.
21
+ - Dark mode is free: `data-theme="dark"` on `<html>` or any subtree
22
+ (`data-theme="light"` pins back). No component has a dark branch.
23
+ - Per-component imports: `@devslab/dds-css/components/button.css` etc.
24
+ - Markup contracts, states, accessibility checklists and do/don't per
25
+ component: [`docs/components.md`](../../docs/components.md).
26
+ Live reference: `preview/components.html` (run `pnpm build` first).
@@ -0,0 +1,47 @@
1
+ /* Avatar โ€” a person or tenant mark: image if there is one, initials if not.
2
+ * Sizes come from the spacing scale (32/40/48) so an avatar lines up with the
3
+ * control of the same size in a list row. The initials fallback uses the
4
+ * brand-subtle pair, which re-tints with the product brand automatically. */
5
+
6
+ .dds-avatar {
7
+ display: inline-flex;
8
+ align-items: center;
9
+ justify-content: center;
10
+ flex: none;
11
+ width: var(--dds-space-40);
12
+ height: var(--dds-space-40);
13
+ border-radius: var(--dds-radius-full);
14
+ overflow: hidden;
15
+ background: var(--dds-color-bg-brand-subtle);
16
+ color: var(--dds-color-text-brand);
17
+ font-family: var(--dds-font-family-sans);
18
+ font-size: var(--dds-typo-label-font-size);
19
+ line-height: var(--dds-typo-label-line-height);
20
+ font-weight: 600;
21
+ user-select: none;
22
+ }
23
+
24
+ .dds-avatar__img {
25
+ width: 100%;
26
+ height: 100%;
27
+ object-fit: cover;
28
+ }
29
+
30
+ .dds-avatar--sm {
31
+ width: var(--dds-space-32);
32
+ height: var(--dds-space-32);
33
+ font-size: var(--dds-typo-caption-font-size);
34
+ line-height: var(--dds-typo-caption-line-height);
35
+ }
36
+
37
+ .dds-avatar--lg {
38
+ width: var(--dds-space-48);
39
+ height: var(--dds-space-48);
40
+ font-size: var(--dds-typo-body-1-font-size);
41
+ line-height: var(--dds-typo-body-1-line-height);
42
+ }
43
+
44
+ /* square variant for tenants/logos โ€” a company mark in a circle gets clipped */
45
+ .dds-avatar--square {
46
+ border-radius: var(--dds-radius-md);
47
+ }
@@ -0,0 +1,47 @@
1
+ /* Badge โ€” brand + the four status pairs. Status colors always ship as
2
+ * foreground + -bg pair (spec ยง3.1); the leading dot repeats the state so
3
+ * color is not the only signal (spec ยง6). Dot size 6px is glyph geometry,
4
+ * not spacing (same 6px as the toast dot โ€” keep them identical). */
5
+
6
+ .dds-badge {
7
+ display: inline-flex;
8
+ align-items: center;
9
+ gap: var(--dds-space-4);
10
+ font-family: var(--dds-font-family-sans);
11
+ font-size: var(--dds-typo-label-font-size);
12
+ line-height: var(--dds-typo-label-line-height);
13
+ font-weight: var(--dds-typo-label-font-weight);
14
+ padding: var(--dds-space-2) var(--dds-space-8);
15
+ border-radius: var(--dds-radius-full);
16
+ white-space: nowrap;
17
+ }
18
+
19
+ .dds-badge::before {
20
+ content: "";
21
+ flex: none;
22
+ width: 6px;
23
+ height: 6px;
24
+ border-radius: var(--dds-radius-full);
25
+ background: currentColor;
26
+ }
27
+
28
+ .dds-badge--brand {
29
+ color: var(--dds-color-text-brand);
30
+ background: var(--dds-color-bg-brand-subtle);
31
+ }
32
+ .dds-badge--success {
33
+ color: var(--dds-color-status-success);
34
+ background: var(--dds-color-status-success-bg);
35
+ }
36
+ .dds-badge--warning {
37
+ color: var(--dds-color-status-warning);
38
+ background: var(--dds-color-status-warning-bg);
39
+ }
40
+ .dds-badge--danger {
41
+ color: var(--dds-color-status-danger);
42
+ background: var(--dds-color-status-danger-bg);
43
+ }
44
+ .dds-badge--info {
45
+ color: var(--dds-color-status-info);
46
+ background: var(--dds-color-status-info-bg);
47
+ }
@@ -0,0 +1,31 @@
1
+ /* dds-css shared plumbing. Components reference ONLY semantic tokens
2
+ * (var(--dds-*)) โ€” tokens.css must be loaded first. */
3
+ [class^="dds-"],
4
+ [class*=" dds-"] {
5
+ box-sizing: border-box;
6
+ }
7
+
8
+ [dir="rtl"] .dds-icon--directional {
9
+ transform: scaleX(-1);
10
+ }
11
+
12
+ @media (prefers-reduced-motion: reduce) {
13
+ :root [class^="dds-"],
14
+ :root [class*=" dds-"] {
15
+ animation-duration: 0.01ms;
16
+ animation-iteration-count: 1;
17
+ scroll-behavior: auto;
18
+ transition-duration: 0.01ms;
19
+ }
20
+ }
21
+
22
+ @media (forced-colors: active) {
23
+ [class^="dds-"],
24
+ [class*=" dds-"] {
25
+ forced-color-adjust: auto;
26
+ }
27
+
28
+ :is([class^="dds-"], [class*=" dds-"]):focus-visible {
29
+ outline-color: Highlight;
30
+ }
31
+ }
@@ -0,0 +1,114 @@
1
+ /* Button โ€” spec ยง4.2: default โ†’ hover โ†’ pressed โ†’ focus-visible โ†’ disabled โ†’ loading.
2
+ * Variants: primary / secondary / ghost / danger. Sizes: sm / md(default) / lg.
3
+ * Control heights come from the spacing scale (32/40/48); type from the typo
4
+ * scale (label for sm/md, body-1 size for lg). Loading = aria-busy="true" with
5
+ * a .dds-spinner child. */
6
+
7
+ .dds-btn {
8
+ appearance: none;
9
+ display: inline-flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+ gap: var(--dds-space-8);
13
+ min-block-size: var(--dds-space-40);
14
+ padding-block: var(--dds-space-8);
15
+ padding-inline: var(--dds-space-16);
16
+ border: 1px solid transparent;
17
+ border-radius: var(--dds-radius-md);
18
+ background: none;
19
+ font-family: var(--dds-font-family-sans);
20
+ font-size: var(--dds-typo-label-font-size);
21
+ line-height: var(--dds-typo-label-line-height);
22
+ font-weight: var(--dds-typo-label-font-weight);
23
+ color: var(--dds-color-text-primary);
24
+ cursor: pointer;
25
+ transition: background-color var(--dds-motion-duration-fast) var(--dds-motion-easing-standard);
26
+ -webkit-tap-highlight-color: transparent;
27
+ white-space: normal;
28
+ overflow-wrap: anywhere;
29
+ }
30
+
31
+ .dds-btn:focus-visible {
32
+ outline: var(--dds-space-2) solid var(--dds-color-border-focus);
33
+ outline-offset: var(--dds-space-2);
34
+ }
35
+
36
+ /* opacity is allowed for disabled only (spec ยง4.2) */
37
+ .dds-btn[disabled],
38
+ .dds-btn[aria-busy="true"] {
39
+ opacity: 0.45;
40
+ cursor: not-allowed;
41
+ }
42
+ .dds-btn[aria-busy="true"] {
43
+ pointer-events: none;
44
+ }
45
+
46
+ /* variants ---------------------------------------------------------------- */
47
+
48
+ .dds-btn--primary {
49
+ background: var(--dds-color-bg-brand);
50
+ color: var(--dds-color-text-on-brand); /* zinc.950, not white โ€” spec ยง3.1 on-brand note */
51
+ font-weight: 600;
52
+ }
53
+ .dds-btn--primary:not([disabled]):not([aria-busy="true"]):hover {
54
+ background: var(--dds-color-bg-brand-hover);
55
+ }
56
+ .dds-btn--primary:not([disabled]):active {
57
+ background: var(--dds-color-bg-brand-hover);
58
+ box-shadow: inset 0 0 0 999px var(--dds-color-overlay-pressed-tint);
59
+ }
60
+
61
+ .dds-btn--secondary {
62
+ border-color: var(--dds-color-border-strong);
63
+ }
64
+ .dds-btn--secondary:not([disabled]):not([aria-busy="true"]):hover {
65
+ background: var(--dds-color-overlay-hover-tint);
66
+ }
67
+ .dds-btn--secondary:not([disabled]):active {
68
+ background: var(--dds-color-overlay-pressed-tint);
69
+ }
70
+
71
+ .dds-btn--ghost {
72
+ color: var(--dds-color-text-brand);
73
+ }
74
+ .dds-btn--ghost:not([disabled]):not([aria-busy="true"]):hover {
75
+ background: var(--dds-color-overlay-hover-tint);
76
+ }
77
+ .dds-btn--ghost:not([disabled]):active {
78
+ background: var(--dds-color-overlay-pressed-tint);
79
+ }
80
+
81
+ .dds-btn--danger {
82
+ background: var(--dds-color-status-danger);
83
+ color: var(--dds-color-text-on-status);
84
+ font-weight: 600;
85
+ }
86
+ .dds-btn--danger:not([disabled]):not([aria-busy="true"]):hover {
87
+ box-shadow: inset 0 0 0 999px var(--dds-color-overlay-hover-tint);
88
+ }
89
+ .dds-btn--danger:not([disabled]):active {
90
+ box-shadow: inset 0 0 0 999px var(--dds-color-overlay-pressed-tint);
91
+ }
92
+
93
+ /* sizes ------------------------------------------------------------------- */
94
+
95
+ .dds-btn--sm {
96
+ min-block-size: var(--dds-space-32);
97
+ padding-block: var(--dds-space-4);
98
+ padding-inline: var(--dds-space-12);
99
+ }
100
+
101
+ .dds-btn--lg {
102
+ min-block-size: var(--dds-space-48);
103
+ padding-block: var(--dds-space-12);
104
+ padding-inline: var(--dds-space-20);
105
+ font-size: var(--dds-typo-body-1-font-size);
106
+ line-height: var(--dds-typo-body-1-line-height);
107
+ }
108
+
109
+ @media (pointer: coarse) {
110
+ .dds-btn,
111
+ .dds-btn--sm {
112
+ min-block-size: 44px;
113
+ }
114
+ }
@@ -0,0 +1,38 @@
1
+ /* Card โ€” a grouped surface. Border + elevated background carry the grouping;
2
+ * elevation.1 is the lift. The same shadow token ships in both themes (as it
3
+ * does for Dialog and Toast): on a zinc.950 page it resolves to almost
4
+ * nothing, which is the intended dark look โ€” a component may not branch on
5
+ * theme (spec ยง3.1: dark is a mapping swap, never an `if (dark)`).
6
+ *
7
+ * --subtle is the flat variant for cards inside an already elevated surface
8
+ * (a card in a dialog), where a second shadow reads as noise. */
9
+
10
+ .dds-card {
11
+ background: var(--dds-color-bg-elevated);
12
+ color: var(--dds-color-text-primary);
13
+ font-family: var(--dds-font-family-sans);
14
+ border: 1px solid var(--dds-color-border-default);
15
+ border-radius: var(--dds-radius-lg);
16
+ box-shadow: var(--dds-elevation-1);
17
+ padding: var(--dds-space-16);
18
+ }
19
+
20
+ .dds-card--subtle {
21
+ background: var(--dds-color-bg-subtle);
22
+ box-shadow: none;
23
+ }
24
+
25
+ .dds-card__title {
26
+ margin: 0 0 var(--dds-space-4);
27
+ font-size: var(--dds-typo-title-3-font-size);
28
+ line-height: var(--dds-typo-title-3-line-height);
29
+ font-weight: var(--dds-typo-title-3-font-weight);
30
+ }
31
+
32
+ .dds-card__body {
33
+ margin: 0;
34
+ font-size: var(--dds-typo-body-2-font-size);
35
+ line-height: var(--dds-typo-body-2-line-height);
36
+ font-weight: var(--dds-typo-body-2-font-weight);
37
+ color: var(--dds-color-text-secondary);
38
+ }
@@ -0,0 +1,91 @@
1
+ /* Checkbox / Radio โ€” one class for both; the input's own type decides the
2
+ * shape (square + tick vs circle + dot), so markup never has to declare the
3
+ * shape twice. The control stays a real <input> (keyboard, form data, AT
4
+ * roles); only its box is redrawn.
5
+ *
6
+ * Box 18px, tick 10ร—6, dot 8px: glyph geometry, not spacing (same allowance
7
+ * as the 6px badge dot). The 18px box sits inside a label whose hit area is
8
+ * the whole row โ€” keep the label text clickable rather than growing the box. */
9
+
10
+ .dds-check {
11
+ display: inline-flex;
12
+ align-items: center;
13
+ gap: var(--dds-space-8);
14
+ font-family: var(--dds-font-family-sans);
15
+ font-size: var(--dds-typo-body-2-font-size);
16
+ line-height: var(--dds-typo-body-2-line-height);
17
+ color: var(--dds-color-text-primary);
18
+ cursor: pointer;
19
+ }
20
+
21
+ .dds-check__input {
22
+ appearance: none;
23
+ flex: none;
24
+ display: grid;
25
+ place-items: center;
26
+ margin: 0;
27
+ width: 18px;
28
+ height: 18px;
29
+ border: 1px solid var(--dds-color-border-strong);
30
+ border-radius: var(--dds-radius-sm);
31
+ background: var(--dds-color-bg-default);
32
+ cursor: inherit;
33
+ transition: background-color var(--dds-motion-duration-fast) var(--dds-motion-easing-standard);
34
+ }
35
+
36
+ .dds-check__input[type="radio"] {
37
+ border-radius: var(--dds-radius-full);
38
+ }
39
+
40
+ .dds-check__input:not([disabled]):hover {
41
+ border-color: var(--dds-color-text-muted);
42
+ }
43
+
44
+ .dds-check__input:checked {
45
+ background: var(--dds-color-bg-brand);
46
+ border-color: var(--dds-color-bg-brand);
47
+ }
48
+
49
+ /* the mark is on-brand (zinc.950), never white โ€” spec ยง3.1 on-brand note */
50
+ .dds-check__input[type="checkbox"]:checked::before {
51
+ content: "";
52
+ width: 10px;
53
+ height: 6px;
54
+ border-inline-start: 2px solid var(--dds-color-text-on-brand);
55
+ border-block-end: 2px solid var(--dds-color-text-on-brand);
56
+ transform: translateY(-1px) rotate(-45deg);
57
+ }
58
+
59
+ .dds-check__input[type="checkbox"]:indeterminate::before {
60
+ content: "";
61
+ width: 10px;
62
+ height: 2px;
63
+ background: var(--dds-color-text-on-brand);
64
+ }
65
+ .dds-check__input[type="checkbox"]:indeterminate {
66
+ background: var(--dds-color-bg-brand);
67
+ border-color: var(--dds-color-bg-brand);
68
+ }
69
+
70
+ .dds-check__input[type="radio"]:checked::before {
71
+ content: "";
72
+ width: 8px;
73
+ height: 8px;
74
+ border-radius: var(--dds-radius-full);
75
+ background: var(--dds-color-text-on-brand);
76
+ }
77
+
78
+ .dds-check__input:focus-visible {
79
+ outline: var(--dds-space-2) solid var(--dds-color-border-focus);
80
+ outline-offset: var(--dds-space-2);
81
+ }
82
+
83
+ /* opacity is allowed for disabled only (spec ยง4.2) โ€” it goes on the label so
84
+ * the box and its text dim once, not twice (nested opacity compounds). */
85
+ .dds-check:has(.dds-check__input[disabled]) {
86
+ opacity: 0.45;
87
+ cursor: not-allowed;
88
+ }
89
+ .dds-check__input[disabled] {
90
+ cursor: not-allowed;
91
+ }
@@ -0,0 +1,51 @@
1
+ /* Chip โ€” a small interactive control: filter toggle, suggested question,
2
+ * removable tag. Badge and Chip are different components on purpose: a badge
3
+ * is state you read, a chip is something you press. Never restyle one into
4
+ * the other (the duplicated-.chip collision this house has already paid for).
5
+ *
6
+ * Selected state is carried by aria-pressed="true" (toggle chips) so the
7
+ * state is announced, not only colored; --selected exists for markup that
8
+ * can't use aria-pressed (e.g. a link chip). */
9
+
10
+ .dds-chip {
11
+ appearance: none;
12
+ display: inline-flex;
13
+ align-items: center;
14
+ gap: var(--dds-space-4);
15
+ font-family: var(--dds-font-family-sans);
16
+ font-size: var(--dds-typo-label-font-size);
17
+ line-height: var(--dds-typo-label-line-height);
18
+ font-weight: var(--dds-typo-label-font-weight);
19
+ padding: var(--dds-space-4) var(--dds-space-12);
20
+ border: 1px solid var(--dds-color-border-strong);
21
+ border-radius: var(--dds-radius-full);
22
+ background: none;
23
+ color: var(--dds-color-text-primary);
24
+ cursor: pointer;
25
+ transition: background-color var(--dds-motion-duration-fast) var(--dds-motion-easing-standard);
26
+ -webkit-tap-highlight-color: transparent;
27
+ }
28
+
29
+ .dds-chip:not([disabled]):hover {
30
+ background: var(--dds-color-overlay-hover-tint);
31
+ }
32
+ .dds-chip:not([disabled]):active {
33
+ background: var(--dds-color-overlay-pressed-tint);
34
+ }
35
+ .dds-chip:focus-visible {
36
+ outline: var(--dds-space-2) solid var(--dds-color-border-focus);
37
+ outline-offset: var(--dds-space-2);
38
+ }
39
+
40
+ .dds-chip[aria-pressed="true"],
41
+ .dds-chip--selected {
42
+ background: var(--dds-color-bg-brand-subtle);
43
+ border-color: var(--dds-color-bg-brand);
44
+ color: var(--dds-color-text-brand);
45
+ }
46
+
47
+ /* opacity is allowed for disabled only (spec ยง4.2) */
48
+ .dds-chip[disabled] {
49
+ opacity: 0.45;
50
+ cursor: not-allowed;
51
+ }
@@ -0,0 +1,58 @@
1
+ /* Dialog โ€” overlay + panel. Two markup shapes are supported:
2
+ * 1. div pattern: .dds-dialog-overlay > .dds-dialog[role="dialog"][aria-modal="true"]
3
+ * 2. native <dialog class="dds-dialog"> (backdrop styled via ::backdrop)
4
+ * Behavior contract (spec ยง5): closes on outside tap and Esc; focus is
5
+ * trapped while open and returns to the opener on close โ€” that is the
6
+ * opener JS's job, this file only owns the look. Label the panel with
7
+ * aria-labelledby pointing at the title. */
8
+
9
+ .dds-dialog-overlay {
10
+ position: fixed;
11
+ inset: 0;
12
+ z-index: 1000;
13
+ display: grid;
14
+ place-items: center;
15
+ padding: var(--dds-space-16);
16
+ background: var(--dds-color-overlay-dim);
17
+ }
18
+
19
+ .dds-dialog {
20
+ background: var(--dds-color-bg-elevated);
21
+ color: var(--dds-color-text-primary);
22
+ font-family: var(--dds-font-family-sans);
23
+ border-radius: var(--dds-radius-lg);
24
+ box-shadow: var(--dds-elevation-3);
25
+ padding: var(--dds-space-20);
26
+ width: 100%;
27
+ max-width: 400px; /* layout default, not a token โ€” override per product */
28
+ }
29
+
30
+ /* native <dialog> resets */
31
+ dialog.dds-dialog {
32
+ border: none;
33
+ margin: auto;
34
+ }
35
+ dialog.dds-dialog::backdrop {
36
+ background: var(--dds-color-overlay-dim);
37
+ }
38
+
39
+ .dds-dialog__title {
40
+ margin: 0 0 var(--dds-space-8);
41
+ font-size: var(--dds-typo-title-2-font-size);
42
+ line-height: var(--dds-typo-title-2-line-height);
43
+ font-weight: var(--dds-typo-title-2-font-weight);
44
+ }
45
+
46
+ .dds-dialog__body {
47
+ margin: 0 0 var(--dds-space-16);
48
+ font-size: var(--dds-typo-body-2-font-size);
49
+ line-height: var(--dds-typo-body-2-line-height);
50
+ font-weight: var(--dds-typo-body-2-font-weight);
51
+ color: var(--dds-color-text-secondary);
52
+ }
53
+
54
+ .dds-dialog__actions {
55
+ display: flex;
56
+ justify-content: flex-end;
57
+ gap: var(--dds-space-8);
58
+ }
@@ -0,0 +1,18 @@
1
+ /* Divider โ€” a rule between sections. Use <hr class="dds-divider"> when the
2
+ * split is meaningful; when it is purely decorative next to text that already
3
+ * separates the groups, add role="presentation".
4
+ * The vertical variant needs a stretchable parent (flex row / grid cell). */
5
+
6
+ .dds-divider {
7
+ border: none;
8
+ border-top: 1px solid var(--dds-color-border-default);
9
+ margin: var(--dds-space-16) 0;
10
+ }
11
+
12
+ .dds-divider--vertical {
13
+ width: 0;
14
+ align-self: stretch;
15
+ border-top: none;
16
+ border-inline-start: 1px solid var(--dds-color-border-default);
17
+ margin: 0 var(--dds-space-16);
18
+ }
@@ -0,0 +1,47 @@
1
+ /* EmptyState โ€” what a list shows when it has nothing yet. House rule: the
2
+ * empty view keeps the same skeleton as the filled one (summary chips and
3
+ * filters stay put) โ€” the empty state replaces the rows, not the screen.
4
+ *
5
+ * Layout note for consumers: give the element `display: none`'s job to
6
+ * [hidden] and never override it with a display rule that lacks a
7
+ * `[hidden]` guard โ€” a shared .empty that did exactly that once painted
8
+ * "no results" on top of a populated table. */
9
+
10
+ .dds-empty {
11
+ display: flex;
12
+ flex-direction: column;
13
+ align-items: center;
14
+ justify-content: center;
15
+ gap: var(--dds-space-8);
16
+ padding: var(--dds-space-40) var(--dds-space-24);
17
+ border: 1px dashed var(--dds-color-border-strong);
18
+ border-radius: var(--dds-radius-lg);
19
+ text-align: center;
20
+ font-family: var(--dds-font-family-sans);
21
+ color: var(--dds-color-text-secondary);
22
+ }
23
+
24
+ .dds-empty[hidden] {
25
+ display: none;
26
+ }
27
+
28
+ .dds-empty__title {
29
+ margin: 0;
30
+ font-size: var(--dds-typo-title-3-font-size);
31
+ line-height: var(--dds-typo-title-3-line-height);
32
+ font-weight: var(--dds-typo-title-3-font-weight);
33
+ color: var(--dds-color-text-primary);
34
+ }
35
+
36
+ .dds-empty__desc {
37
+ margin: 0;
38
+ max-width: 36em;
39
+ font-size: var(--dds-typo-body-2-font-size);
40
+ line-height: var(--dds-typo-body-2-line-height);
41
+ }
42
+
43
+ .dds-empty__actions {
44
+ display: flex;
45
+ gap: var(--dds-space-8);
46
+ margin-top: var(--dds-space-8);
47
+ }