@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 +74 -0
- package/README.ko.md +25 -0
- package/README.md +26 -0
- package/dist/components/avatar.css +47 -0
- package/dist/components/badge.css +47 -0
- package/dist/components/base.css +31 -0
- package/dist/components/button.css +114 -0
- package/dist/components/card.css +38 -0
- package/dist/components/checkbox-radio.css +91 -0
- package/dist/components/chip.css +51 -0
- package/dist/components/dialog.css +58 -0
- package/dist/components/divider.css +18 -0
- package/dist/components/emptystate.css +47 -0
- package/dist/components/iconbutton.css +71 -0
- package/dist/components/listrow.css +81 -0
- package/dist/components/select.css +66 -0
- package/dist/components/skeleton.css +32 -0
- package/dist/components/spinner.css +34 -0
- package/dist/components/switch.css +78 -0
- package/dist/components/tabs.css +59 -0
- package/dist/components/textarea.css +42 -0
- package/dist/components/textfield.css +63 -0
- package/dist/components/toast.css +72 -0
- package/dist/components/tooltip.css +43 -0
- package/dist/dds.css +1206 -0
- package/package.json +28 -0
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
|
+
}
|