@kwtv/components 0.2.0 → 0.4.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.
Files changed (56) hide show
  1. package/README.md +52 -9
  2. package/package.json +2 -2
  3. package/src/astro/Accordion.astro +10 -6
  4. package/src/astro/AccordionItem.astro +55 -45
  5. package/src/astro/ActiveFilterTags.astro +41 -28
  6. package/src/astro/Alert.astro +45 -41
  7. package/src/astro/Badge.astro +80 -76
  8. package/src/astro/Band.astro +104 -58
  9. package/src/astro/Breadcrumb.astro +38 -30
  10. package/src/astro/Button.astro +135 -129
  11. package/src/astro/Card.astro +72 -62
  12. package/src/astro/CartLine.astro +72 -58
  13. package/src/astro/Checkbox.astro +36 -32
  14. package/src/astro/Chip.astro +68 -64
  15. package/src/astro/ChipRail.astro +6 -1
  16. package/src/astro/DotGrid.astro +55 -42
  17. package/src/astro/EmptyState.astro +29 -23
  18. package/src/astro/FilterGroup.astro +29 -26
  19. package/src/astro/Filters.astro +57 -47
  20. package/src/astro/Footer.astro +195 -178
  21. package/src/astro/FooterColumn.astro +39 -35
  22. package/src/astro/Header.astro +80 -67
  23. package/src/astro/Icon.astro +4 -2
  24. package/src/astro/IconButton.astro +67 -61
  25. package/src/astro/Marquee.astro +43 -41
  26. package/src/astro/NavDropdown.astro +169 -149
  27. package/src/astro/NavLink.astro +59 -56
  28. package/src/astro/Newsletter.astro +64 -56
  29. package/src/astro/Pager.astro +71 -46
  30. package/src/astro/PickCard.astro +72 -68
  31. package/src/astro/ProductCard.astro +134 -119
  32. package/src/astro/Radio.astro +36 -32
  33. package/src/astro/Skeleton.astro +60 -42
  34. package/src/astro/Social.astro +26 -21
  35. package/src/astro/StepIndicator.astro +70 -54
  36. package/src/astro/Sticker.astro +74 -49
  37. package/src/astro/Swatch.astro +44 -40
  38. package/src/astro/SwipeCarousel.astro +5 -1
  39. package/src/astro/Table.astro +66 -56
  40. package/src/astro/TableCard.astro +45 -37
  41. package/src/astro/TextField.astro +104 -91
  42. package/src/astro/Totals.astro +39 -35
  43. package/src/react/Drawer.css +60 -56
  44. package/src/react/Drawer.tsx +1 -5
  45. package/src/react/Modal.css +97 -93
  46. package/src/react/Modal.tsx +4 -6
  47. package/src/react/NewsCarousel.css +108 -104
  48. package/src/react/NewsCarousel.tsx +7 -1
  49. package/src/react/QuantityStepper.css +84 -80
  50. package/src/react/Select.css +159 -155
  51. package/src/react/Select.tsx +8 -2
  52. package/src/react/Toast.css +128 -124
  53. package/src/react/Toast.tsx +38 -4
  54. package/src/styles/base.css +30 -26
  55. package/src/styles/controls.css +183 -179
  56. package/src/styles/motion.css +203 -199
@@ -25,7 +25,11 @@ const {
25
25
 
26
26
  const Title = titleTag;
27
27
 
28
- const classes = ["ui-card", variant !== "default" && `ui-card--${variant}`, className]
28
+ const classes = [
29
+ "ui-card",
30
+ variant !== "default" && `ui-card--${variant}`,
31
+ className,
32
+ ]
29
33
  .filter(Boolean)
30
34
  .join(" ");
31
35
 
@@ -35,7 +39,9 @@ const hasFooter = Astro.slots.has("footer");
35
39
  <div class={classes} {...rest}>
36
40
  {eyebrow && <span class="ui-card__eyebrow">{eyebrow}</span>}
37
41
  {title && <Title class="ui-card__title">{title}</Title>}
38
- <span class="ui-card__body"><slot /></span>
42
+ <span class="ui-card__body">
43
+ <slot />
44
+ </span>
39
45
  {hasFooter && (
40
46
  <div class="ui-card__footer">
41
47
  <slot name="footer" />
@@ -44,72 +50,76 @@ const hasFooter = Astro.slots.has("footer");
44
50
  </div>
45
51
 
46
52
  <style>
47
- .ui-card {
48
- display: flex;
49
- flex-direction: column;
50
- gap: var(--space-2);
51
- padding: var(--space-5);
52
- border: 1px solid var(--border);
53
- border-radius: var(--radius-lg);
54
- background: var(--surface);
55
- color: var(--text);
56
- }
53
+ @layer reset, tokens, base, components, motion, utilities;
57
54
 
58
- .ui-card__eyebrow {
59
- font-family: var(--font-mono);
60
- font-size: var(--text-2xs);
61
- line-height: var(--leading-2xs);
62
- font-weight: var(--weight-medium);
63
- letter-spacing: var(--tracking-widest);
64
- text-transform: uppercase;
65
- color: var(--text-muted);
66
- }
55
+ @layer components {
56
+ .ui-card {
57
+ display: flex;
58
+ flex-direction: column;
59
+ gap: var(--space-2);
60
+ padding: var(--space-5);
61
+ border: 1px solid var(--border);
62
+ border-radius: var(--radius-lg);
63
+ background: var(--surface);
64
+ color: var(--text);
65
+ }
67
66
 
68
- .ui-card__title {
69
- margin: 0;
70
- font-size: var(--text-lg);
71
- line-height: var(--leading-lg);
72
- font-weight: var(--weight-semibold);
73
- letter-spacing: var(--tracking-tight);
74
- color: var(--text);
75
- }
67
+ .ui-card__eyebrow {
68
+ font-family: var(--font-mono);
69
+ font-size: var(--text-2xs);
70
+ line-height: var(--leading-2xs);
71
+ font-weight: var(--weight-medium);
72
+ letter-spacing: var(--tracking-widest);
73
+ text-transform: uppercase;
74
+ color: var(--text-muted);
75
+ }
76
76
 
77
- .ui-card__body {
78
- font-size: var(--text-sm);
79
- line-height: var(--leading-sm);
80
- color: var(--text-muted);
81
- }
77
+ .ui-card__title {
78
+ margin: 0;
79
+ font-size: var(--text-lg);
80
+ line-height: var(--leading-lg);
81
+ font-weight: var(--weight-semibold);
82
+ letter-spacing: var(--tracking-tight);
83
+ color: var(--text);
84
+ }
82
85
 
83
- .ui-card__footer {
84
- display: flex;
85
- flex-wrap: wrap;
86
- gap: var(--space-2);
87
- margin-top: var(--space-2);
88
- }
86
+ .ui-card__body {
87
+ font-size: var(--text-sm);
88
+ line-height: var(--leading-sm);
89
+ color: var(--text-muted);
90
+ }
89
91
 
90
- /* Raised: cart summary, order total. */
91
- .ui-card--raised {
92
- border-color: transparent;
93
- box-shadow: var(--shadow-lg);
94
- }
92
+ .ui-card__footer {
93
+ display: flex;
94
+ flex-wrap: wrap;
95
+ gap: var(--space-2);
96
+ margin-top: var(--space-2);
97
+ }
95
98
 
96
- /* Accent: sage surface, ink-dark text kept at 4.5:1. Buttons inside keep
97
- their own foreground colour. */
98
- .ui-card--accent {
99
- background: var(--surface-accent);
100
- border-color: var(--border-strong);
101
- color: var(--surface-accent-fg);
102
- }
103
- .ui-card--accent .ui-card__eyebrow,
104
- .ui-card--accent .ui-card__title,
105
- .ui-card--accent .ui-card__body {
106
- color: var(--surface-accent-fg);
107
- }
99
+ /* Raised: cart summary, order total. */
100
+ .ui-card--raised {
101
+ border-color: transparent;
102
+ box-shadow: var(--shadow-lg);
103
+ }
104
+
105
+ /* Accent: sage surface, ink-dark text kept at 4.5:1. Buttons inside keep */
106
+ /* their own foreground colour. */
107
+ .ui-card--accent {
108
+ background: var(--surface-accent);
109
+ border-color: var(--border-strong);
110
+ color: var(--surface-accent-fg);
111
+ }
112
+ .ui-card--accent .ui-card__eyebrow,
113
+ .ui-card--accent .ui-card__title,
114
+ .ui-card--accent .ui-card__body {
115
+ color: var(--surface-accent-fg);
116
+ }
108
117
 
109
- /* Flat: no fill, grouping only. */
110
- .ui-card--flat {
111
- background: transparent;
112
- border-color: transparent;
113
- padding-inline: 0;
118
+ /* Flat: no fill, grouping only. */
119
+ .ui-card--flat {
120
+ background: transparent;
121
+ border-color: transparent;
122
+ padding-inline: 0;
123
+ }
114
124
  }
115
125
  </style>
@@ -35,78 +35,92 @@ const classes = ["ui-line", className].filter(Boolean).join(" ");
35
35
  ---
36
36
 
37
37
  <div class={classes} {...rest}>
38
- <img class="ui-line__thumb" loading="lazy" decoding="async" src={img} alt={alt} />
38
+ <img
39
+ class="ui-line__thumb"
40
+ loading="lazy"
41
+ decoding="async"
42
+ src={img}
43
+ alt={alt}
44
+ />
39
45
  <span class="ui-line__grow">
40
46
  <b class="ui-line__title">{title}</b>
41
47
  {note && <span class="ui-line__note">{note}</span>}
42
- <a class="ui-line__remove" href={removeHref}>{removeLabel}</a>
48
+ <a class="ui-line__remove" href={removeHref}>
49
+ {removeLabel}
50
+ </a>
51
+ </span>
52
+ <span class="ui-line__qty">
53
+ <slot name="qty" />
43
54
  </span>
44
- <span class="ui-line__qty"><slot name="qty" /></span>
45
55
  <b class="ui-line__price">{price}</b>
46
56
  </div>
47
57
 
48
58
  <style>
49
- .ui-line {
50
- display: flex;
51
- align-items: flex-start;
52
- gap: var(--space-4);
53
- padding-block: var(--space-4-5);
54
- border-top: 1px solid var(--border);
55
- }
59
+ @layer reset, tokens, base, components, motion, utilities;
56
60
 
57
- .ui-line__thumb {
58
- flex-shrink: 0;
59
- width: 72px;
60
- height: 72px;
61
- object-fit: cover;
62
- border-radius: var(--radius-md);
63
- border: 1px solid var(--border);
64
- }
61
+ @layer components {
62
+ .ui-line {
63
+ display: flex;
64
+ align-items: flex-start;
65
+ gap: var(--space-4);
66
+ padding-block: var(--space-4-5);
67
+ border-top: 1px solid var(--border);
68
+ }
65
69
 
66
- .ui-line__grow {
67
- display: flex;
68
- flex-direction: column;
69
- gap: var(--space-1);
70
- flex: 1 1 auto;
71
- min-width: 0;
72
- }
70
+ .ui-line__thumb {
71
+ flex-shrink: 0;
72
+ width: 72px;
73
+ height: 72px;
74
+ object-fit: cover;
75
+ border-radius: var(--radius-md);
76
+ border: 1px solid var(--border);
77
+ }
73
78
 
74
- .ui-line__title {
75
- font-family: var(--font-sans);
76
- font-size: var(--text-base);
77
- font-weight: var(--weight-semibold);
78
- color: var(--text);
79
- }
79
+ .ui-line__grow {
80
+ display: flex;
81
+ flex-direction: column;
82
+ gap: var(--space-1);
83
+ flex: 1 1 auto;
84
+ min-width: 0;
85
+ }
80
86
 
81
- .ui-line__note {
82
- font-size: var(--text-sm);
83
- color: var(--text-muted);
84
- }
87
+ .ui-line__title {
88
+ font-family: var(--font-sans);
89
+ font-size: var(--text-base);
90
+ font-weight: var(--weight-semibold);
91
+ color: var(--text);
92
+ }
85
93
 
86
- .ui-line__remove {
87
- align-self: flex-start;
88
- font-size: var(--text-xs);
89
- color: var(--link);
90
- }
91
- .ui-line__remove:hover {
92
- color: var(--link-hover);
93
- }
94
- .ui-line__remove:focus-visible {
95
- outline: 2px solid var(--focus);
96
- outline-offset: 2px;
97
- box-shadow: 0 0 0 4px var(--focus-outer);
98
- border-radius: var(--radius-sm);
99
- }
94
+ .ui-line__note {
95
+ font-size: var(--text-sm);
96
+ color: var(--text-muted);
97
+ }
100
98
 
101
- .ui-line__qty {
102
- flex-shrink: 0;
103
- }
99
+ .ui-line__remove {
100
+ align-self: flex-start;
101
+ font-size: var(--text-xs);
102
+ color: var(--link);
103
+ }
104
+ .ui-line__remove:hover {
105
+ color: var(--link-hover);
106
+ }
107
+ .ui-line__remove:focus-visible {
108
+ outline: 2px solid var(--focus);
109
+ outline-offset: 2px;
110
+ box-shadow: 0 0 0 4px var(--focus-outer);
111
+ border-radius: var(--radius-sm);
112
+ }
113
+
114
+ .ui-line__qty {
115
+ flex-shrink: 0;
116
+ }
104
117
 
105
- .ui-line__price {
106
- flex-shrink: 0;
107
- font-size: var(--text-base);
108
- font-weight: var(--weight-semibold);
109
- font-variant-numeric: tabular-nums;
110
- color: var(--text);
118
+ .ui-line__price {
119
+ flex-shrink: 0;
120
+ font-size: var(--text-base);
121
+ font-weight: var(--weight-semibold);
122
+ font-variant-numeric: tabular-nums;
123
+ color: var(--text);
124
+ }
111
125
  }
112
126
  </style>
@@ -47,43 +47,47 @@ const classes = ["ui-check", disabled && "is-disabled", className]
47
47
  </label>
48
48
 
49
49
  <style>
50
- .ui-check {
51
- display: flex;
52
- align-items: center;
53
- gap: var(--space-2);
54
- min-height: 32px;
55
- font-family: var(--font-sans);
56
- font-size: var(--text-sm);
57
- color: var(--text);
58
- cursor: pointer;
59
- }
50
+ @layer reset, tokens, base, components, motion, utilities;
60
51
 
61
- .ui-check input {
62
- width: 17px;
63
- height: 17px;
64
- flex-shrink: 0;
65
- accent-color: var(--primary);
66
- cursor: pointer;
67
- }
52
+ @layer components {
53
+ .ui-check {
54
+ display: flex;
55
+ align-items: center;
56
+ gap: var(--space-2);
57
+ min-height: 32px;
58
+ font-family: var(--font-sans);
59
+ font-size: var(--text-sm);
60
+ color: var(--text);
61
+ cursor: pointer;
62
+ }
68
63
 
69
- .ui-check input:focus-visible {
70
- outline: 2px solid var(--focus);
71
- outline-offset: 2px;
72
- box-shadow: 0 0 0 4px var(--focus-outer);
73
- }
64
+ .ui-check input {
65
+ width: 17px;
66
+ height: 17px;
67
+ flex-shrink: 0;
68
+ accent-color: var(--primary);
69
+ cursor: pointer;
70
+ }
74
71
 
75
- .ui-check.is-disabled {
76
- cursor: not-allowed;
77
- color: var(--text-muted);
78
- }
72
+ .ui-check input:focus-visible {
73
+ outline: 2px solid var(--focus);
74
+ outline-offset: 2px;
75
+ box-shadow: 0 0 0 4px var(--focus-outer);
76
+ }
79
77
 
80
- .ui-check.is-disabled input {
81
- cursor: not-allowed;
82
- }
78
+ .ui-check.is-disabled {
79
+ cursor: not-allowed;
80
+ color: var(--text-muted);
81
+ }
83
82
 
84
- @media (max-width: 700px), (pointer: coarse) {
85
- .ui-check {
86
- min-height: 40px;
83
+ .ui-check.is-disabled input {
84
+ cursor: not-allowed;
85
+ }
86
+
87
+ @media (max-width: 700px), (pointer: coarse) {
88
+ .ui-check {
89
+ min-height: 40px;
90
+ }
87
91
  }
88
92
  }
89
93
  </style>
@@ -50,81 +50,85 @@ const classes = [
50
50
  </button>
51
51
 
52
52
  <style>
53
- .ui-chip {
54
- display: inline-flex;
55
- align-items: center;
56
- justify-content: center;
57
- min-height: 36px;
58
- padding: 0 var(--space-4);
59
- background: var(--surface);
60
- border: 1px solid var(--border);
61
- border-radius: var(--radius-md);
62
- font-family: var(--font-sans);
63
- font-size: var(--text-sm);
64
- font-weight: var(--weight-medium);
65
- color: var(--text);
66
- cursor: pointer;
67
- transition:
68
- border-color var(--duration-fast) var(--ease-standard),
69
- background-color var(--duration-fast) var(--ease-standard);
70
- }
53
+ @layer reset, tokens, base, components, motion, utilities;
71
54
 
72
- .ui-chip:hover,
73
- .ui-chip.is-hover {
74
- border-color: var(--border-strong);
75
- }
55
+ @layer components {
56
+ .ui-chip {
57
+ display: inline-flex;
58
+ align-items: center;
59
+ justify-content: center;
60
+ min-height: 36px;
61
+ padding: 0 var(--space-4);
62
+ background: var(--surface);
63
+ border: 1px solid var(--border);
64
+ border-radius: var(--radius-md);
65
+ font-family: var(--font-sans);
66
+ font-size: var(--text-sm);
67
+ font-weight: var(--weight-medium);
68
+ color: var(--text);
69
+ cursor: pointer;
70
+ transition:
71
+ border-color var(--duration-fast) var(--ease-standard),
72
+ background-color var(--duration-fast) var(--ease-standard);
73
+ }
76
74
 
77
- .ui-chip:focus-visible,
78
- .ui-chip.is-focus {
79
- outline: 2px solid var(--focus);
80
- outline-offset: 2px;
81
- box-shadow: 0 0 0 4px var(--focus-outer);
82
- }
75
+ .ui-chip:hover,
76
+ .ui-chip.is-hover {
77
+ border-color: var(--border-strong);
78
+ }
83
79
 
84
- /* Selected: 2 px border plus the accent surface, readable without colour. */
85
- .ui-chip.is-selected,
86
- .ui-chip[aria-pressed="true"] {
87
- border-width: 2px;
88
- border-color: var(--border-strong);
89
- background: var(--surface-accent);
90
- color: var(--surface-accent-fg, var(--text));
91
- font-weight: var(--weight-semibold);
92
- }
80
+ .ui-chip:focus-visible,
81
+ .ui-chip.is-focus {
82
+ outline: 2px solid var(--focus);
83
+ outline-offset: 2px;
84
+ box-shadow: 0 0 0 4px var(--focus-outer);
85
+ }
93
86
 
94
- .ui-chip.is-disabled,
95
- .ui-chip:disabled {
96
- opacity: 0.5;
97
- cursor: not-allowed;
98
- }
87
+ /* Selected: 2 px border plus the accent surface, readable without colour. */
88
+ .ui-chip.is-selected,
89
+ .ui-chip[aria-pressed="true"] {
90
+ border-width: 2px;
91
+ border-color: var(--border-strong);
92
+ background: var(--surface-accent);
93
+ color: var(--surface-accent-fg, var(--text));
94
+ font-weight: var(--weight-semibold);
95
+ }
99
96
 
100
- .ui-chip--struck {
101
- text-decoration: line-through;
102
- }
97
+ .ui-chip.is-disabled,
98
+ .ui-chip:disabled {
99
+ opacity: 0.5;
100
+ cursor: not-allowed;
101
+ }
103
102
 
104
- /* Filter chip: 32 px. */
105
- .ui-chip--filter {
106
- min-height: 32px;
107
- border-radius: var(--radius-full);
108
- padding: 0 var(--space-3);
109
- }
103
+ .ui-chip--struck {
104
+ text-decoration: line-through;
105
+ }
110
106
 
111
- .ui-chip--filter.is-selected,
112
- .ui-chip--filter[aria-pressed="true"] {
113
- background: var(--surface-accent);
114
- }
107
+ /* Filter chip: 32 px. */
108
+ .ui-chip--filter {
109
+ min-height: 32px;
110
+ border-radius: var(--radius-full);
111
+ padding: 0 var(--space-3);
112
+ }
115
113
 
116
- @media (prefers-reduced-motion: reduce) {
117
- .ui-chip {
118
- transition: none;
114
+ .ui-chip--filter.is-selected,
115
+ .ui-chip--filter[aria-pressed="true"] {
116
+ background: var(--surface-accent);
119
117
  }
120
- }
121
118
 
122
- @media (max-width: 700px), (pointer: coarse) {
123
- .ui-chip {
124
- min-height: 36px;
119
+ @media (prefers-reduced-motion: reduce) {
120
+ .ui-chip {
121
+ transition: none;
122
+ }
125
123
  }
126
- .ui-chip--filter {
127
- min-height: 40px;
124
+
125
+ @media (max-width: 700px), (pointer: coarse) {
126
+ .ui-chip {
127
+ min-height: 36px;
128
+ }
129
+ .ui-chip--filter {
130
+ min-height: 40px;
131
+ }
128
132
  }
129
133
  }
130
134
  </style>
@@ -17,7 +17,12 @@ const classes = ["pl-chips", className].filter(Boolean).join(" ");
17
17
 
18
18
  <nav class={classes} aria-label={label} {...rest}>
19
19
  {items.map((item) => (
20
- <a class="pl-chip" href={item.href} data-on={item.current ? "1" : undefined} aria-current={item.current ? "page" : undefined}>
20
+ <a
21
+ class="pl-chip"
22
+ href={item.href}
23
+ data-on={item.current ? "1" : undefined}
24
+ aria-current={item.current ? "page" : undefined}
25
+ >
21
26
  {item.label}
22
27
  </a>
23
28
  ))}
@@ -16,56 +16,69 @@ const classes = ["pl-dotgrid", compact && "pl-dotgrid--compact", className]
16
16
 
17
17
  <div class={classes} {...rest}>
18
18
  <span class="pl-dotgrid__dots" aria-hidden="true"></span>
19
- <span class="pl-dotgrid__content"><slot /></span>
19
+ <span class="pl-dotgrid__content">
20
+ <slot />
21
+ </span>
20
22
  </div>
21
23
 
22
24
  <style>
23
- .pl-dotgrid {
24
- position: relative;
25
- min-height: 132px;
26
- overflow: hidden;
27
- border: 1px solid var(--border);
28
- border-radius: var(--radius-lg);
29
- background: var(--surface);
30
- }
25
+ @layer reset, tokens, base, components, motion, utilities;
31
26
 
32
- .pl-dotgrid__dots {
33
- position: absolute;
34
- inset: 0;
35
- background-image: radial-gradient(var(--dotgrid-color, var(--sage-light)) 1.4px, transparent 1.6px);
36
- background-size: 28px 28px;
37
- opacity: var(--dotgrid-opacity, var(--dotgrid-theme-opacity, .7));
38
- animation: pl-drift 6s linear infinite;
39
- }
27
+ @layer components {
28
+ .pl-dotgrid {
29
+ position: relative;
30
+ min-height: 132px;
31
+ overflow: hidden;
32
+ border: 1px solid var(--border);
33
+ border-radius: var(--radius-lg);
34
+ background: var(--surface);
35
+ }
40
36
 
41
- .pl-dotgrid__content {
42
- position: relative;
43
- display: block;
44
- padding: var(--space-5);
45
- font-size: var(--text-lg);
46
- font-weight: var(--weight-semibold);
47
- }
37
+ .pl-dotgrid__dots {
38
+ position: absolute;
39
+ inset: 0;
40
+ background-image: radial-gradient(
41
+ var(--dotgrid-color, var(--sage-light)) 1.4px,
42
+ transparent 1.6px
43
+ );
44
+ background-size: 28px 28px;
45
+ opacity: var(--dotgrid-opacity, var(--dotgrid-theme-opacity, 0.7));
46
+ animation: pl-drift 6s linear infinite;
47
+ }
48
48
 
49
- .pl-dotgrid--compact {
50
- border: 0;
51
- border-radius: 0;
52
- }
53
- .pl-dotgrid--compact .pl-dotgrid__dots {
54
- background-size: 24px 24px;
55
- }
56
- .pl-dotgrid--compact .pl-dotgrid__content {
57
- font-size: var(--text-base);
58
- }
49
+ .pl-dotgrid__content {
50
+ position: relative;
51
+ display: block;
52
+ padding: var(--space-5);
53
+ font-size: var(--text-lg);
54
+ font-weight: var(--weight-semibold);
55
+ }
59
56
 
60
- /* Explicit themes win over OS preference, as in the token cascade. */
61
- :global([data-theme="dark"]) .pl-dotgrid { --dotgrid-theme-opacity: .2; }
62
- @media (prefers-color-scheme: dark) {
63
- :global(:root:not([data-theme])) .pl-dotgrid { --dotgrid-theme-opacity: .2; }
64
- }
57
+ .pl-dotgrid--compact {
58
+ border: 0;
59
+ border-radius: 0;
60
+ }
61
+ .pl-dotgrid--compact .pl-dotgrid__dots {
62
+ background-size: 24px 24px;
63
+ }
64
+ .pl-dotgrid--compact .pl-dotgrid__content {
65
+ font-size: var(--text-base);
66
+ }
65
67
 
66
- @media (prefers-reduced-motion: reduce) {
67
- .pl-dotgrid__dots {
68
- animation: none;
68
+ /* Explicit themes win over OS preference, as in the token cascade. */
69
+ :global([data-theme="dark"]) .pl-dotgrid {
70
+ --dotgrid-theme-opacity: 0.2;
71
+ }
72
+ @media (prefers-color-scheme: dark) {
73
+ :global(:root:not([data-theme])) .pl-dotgrid {
74
+ --dotgrid-theme-opacity: 0.2;
75
+ }
76
+ }
77
+
78
+ @media (prefers-reduced-motion: reduce) {
79
+ .pl-dotgrid__dots {
80
+ animation: none;
81
+ }
69
82
  }
70
83
  }
71
84
  </style>