@kenlo/sds-design-system 0.9.9
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.md +129 -0
- package/components/Accordion/Accordion.css +186 -0
- package/components/Accordion/accordion-variants.js +17 -0
- package/components/Accordion/accordion.marko +264 -0
- package/components/AccordionGroup/AccordionGroup.css +35 -0
- package/components/AccordionGroup/accordion-group.marko +17 -0
- package/components/Alert/Alert.css +104 -0
- package/components/Alert/alert-variants.js +35 -0
- package/components/Alert/alert.marko +94 -0
- package/components/AttachmentList/AttachmentList.css +92 -0
- package/components/AttachmentList/attachment-list.marko +51 -0
- package/components/AttachmentList/attachment-row.marko +167 -0
- package/components/Avatar/Avatar.css +110 -0
- package/components/Avatar/avatar-variants.js +12 -0
- package/components/Avatar/avatar.marko +71 -0
- package/components/Badge/Badge.css +381 -0
- package/components/Badge/badge-dismissible.marko +143 -0
- package/components/Badge/badge-variants.js +35 -0
- package/components/Badge/badge.marko +134 -0
- package/components/BottomSheet/BottomSheet.css +300 -0
- package/components/BottomSheet/bottom-sheet.marko +340 -0
- package/components/BulkActionBar/BulkActionBar.css +88 -0
- package/components/BulkActionBar/bulk-action-bar.marko +186 -0
- package/components/Button/Button.css +340 -0
- package/components/Button/button-variants.js +25 -0
- package/components/Button/button.marko +139 -0
- package/components/Card/Card.css +391 -0
- package/components/Card/card-body.marko +5 -0
- package/components/Card/card-collapsible.marko +94 -0
- package/components/Card/card-footer.marko +12 -0
- package/components/Card/card-header.marko +30 -0
- package/components/Card/card-info-pair.marko +17 -0
- package/components/Card/card-info-row.marko +12 -0
- package/components/Card/card-section.marko +14 -0
- package/components/Card/card-variants.js +24 -0
- package/components/Card/card.marko +124 -0
- package/components/Checkbox/Checkbox.css +249 -0
- package/components/Checkbox/checkbox-variants.js +21 -0
- package/components/Checkbox/checkbox.marko +128 -0
- package/components/ColorPicker/ColorPicker.css +253 -0
- package/components/ColorPicker/color-maths.js +104 -0
- package/components/ColorPicker/color-picker.marko +474 -0
- package/components/DatePicker/DatePicker.css +429 -0
- package/components/DatePicker/date-logic.js +225 -0
- package/components/DatePicker/date-picker.marko +492 -0
- package/components/Drawer/Drawer.css +174 -0
- package/components/Drawer/drawer.marko +262 -0
- package/components/Floating/Floating.css +28 -0
- package/components/Floating/floating-position.js +251 -0
- package/components/Floating/floating.marko +291 -0
- package/components/Form/Form.css +15 -0
- package/components/Form/form-context.js +69 -0
- package/components/Form/form.marko +73 -0
- package/components/GradientPicker/GradientPicker.css +179 -0
- package/components/GradientPicker/gradient-picker.marko +398 -0
- package/components/Grid/Grid.css +161 -0
- package/components/Grid/col.marko +36 -0
- package/components/Grid/grid-variants.js +43 -0
- package/components/Grid/grid.marko +25 -0
- package/components/Header/Header.css +457 -0
- package/components/Header/header.marko +523 -0
- package/components/Icon/Icon.css +44 -0
- package/components/Icon/icon-colors.js +31 -0
- package/components/Icon/icon.marko +56 -0
- package/components/IconButton/IconButton.css +85 -0
- package/components/IconButton/icon-button-variants.js +18 -0
- package/components/IconButton/icon-button.marko +103 -0
- package/components/Incrementer/Incrementer.css +94 -0
- package/components/Incrementer/incrementer.marko +70 -0
- package/components/Input/Input.css +547 -0
- package/components/Input/input-countries.js +109 -0
- package/components/Input/input-masks.js +218 -0
- package/components/Input/input-types.js +194 -0
- package/components/Input/input.marko +530 -0
- package/components/Menu/Menu.css +651 -0
- package/components/Menu/menu-variants.js +89 -0
- package/components/Menu/menu.marko +560 -0
- package/components/Modal/Modal.css +160 -0
- package/components/Modal/modal.marko +296 -0
- package/components/Nav/Nav.css +298 -0
- package/components/Nav/nav-variants.js +23 -0
- package/components/Nav/nav.marko +164 -0
- package/components/Notification/Notification.css +78 -0
- package/components/Notification/notification-variants.js +30 -0
- package/components/Notification/notification.marko +55 -0
- package/components/Pagination/Pagination.css +78 -0
- package/components/Pagination/pagination-pages.js +66 -0
- package/components/Pagination/pagination.marko +141 -0
- package/components/Pill/Pill.css +193 -0
- package/components/Pill/pill-variants.js +16 -0
- package/components/Pill/pill.marko +165 -0
- package/components/Popover/Popover.css +121 -0
- package/components/Popover/popover.marko +96 -0
- package/components/Radio/Radio.css +230 -0
- package/components/Radio/radio-variants.js +16 -0
- package/components/Radio/radio.marko +93 -0
- package/components/RadioGroup/RadioGroup.css +32 -0
- package/components/RadioGroup/radio-group.marko +93 -0
- package/components/SearchBar/SearchBar.css +181 -0
- package/components/SearchBar/search-bar-variants.js +24 -0
- package/components/SearchBar/search-bar.marko +157 -0
- package/components/Select/Select.css +427 -0
- package/components/Select/select-options.js +163 -0
- package/components/Select/select.marko +449 -0
- package/components/Separator/Separator.css +22 -0
- package/components/Separator/separator.marko +14 -0
- package/components/Skeleton/Skeleton.css +100 -0
- package/components/Skeleton/skeleton-avatar.marko +28 -0
- package/components/Skeleton/skeleton-button.marko +22 -0
- package/components/Skeleton/skeleton-image.marko +20 -0
- package/components/Skeleton/skeleton-input.marko +22 -0
- package/components/Skeleton/skeleton-variants.js +16 -0
- package/components/Skeleton/skeleton.marko +53 -0
- package/components/Spinner/Spinner.css +173 -0
- package/components/Spinner/spinner-geometry.js +39 -0
- package/components/Spinner/spinner-overlay.marko +97 -0
- package/components/Spinner/spinner.marko +88 -0
- package/components/StatCard/StatCard.css +90 -0
- package/components/StatCard/stat-card-group.marko +5 -0
- package/components/StatCard/stat-card.marko +57 -0
- package/components/Switch/Switch.css +209 -0
- package/components/Switch/switch.marko +99 -0
- package/components/Table/Table.css +270 -0
- package/components/Table/table-cell.marko +28 -0
- package/components/Table/table-header.marko +27 -0
- package/components/Table/table-logic.js +129 -0
- package/components/Table/table.marko +372 -0
- package/components/Text/Text.css +79 -0
- package/components/Text/text-variants.js +25 -0
- package/components/Text/text.marko +48 -0
- package/components/TextEditor/TextEditor.css +78 -0
- package/components/TextEditor/safe-url.js +50 -0
- package/components/TextEditor/text-editor-commands.js +22 -0
- package/components/TextEditor/text-editor.marko +253 -0
- package/components/Toast/Toast.css +88 -0
- package/components/Toast/toast-api.js +19 -0
- package/components/Toast/toast-store.js +118 -0
- package/components/Toast/toast-variants.js +16 -0
- package/components/Toast/toast.marko +93 -0
- package/components/Toast/toaster.marko +70 -0
- package/components/Toggle/Toggle.css +169 -0
- package/components/Toggle/toggle.marko +93 -0
- package/components/Tooltip/Tooltip.css +115 -0
- package/components/Tooltip/tooltip.marko +77 -0
- package/components/Uploader/Uploader.css +180 -0
- package/components/Uploader/crop-geometry.js +161 -0
- package/components/Uploader/uploader.marko +269 -0
- package/foundations/Scrollbar/Scrollbar.css +26 -0
- package/foundations/attrs.js +27 -0
- package/foundations/breakpoints.js +27 -0
- package/foundations/contrast.js +74 -0
- package/foundations/custom-scheme.js +222 -0
- package/foundations/overlay.js +108 -0
- package/foundations/styles.css +284 -0
- package/foundations/text.js +22 -0
- package/foundations/theme-server.js +81 -0
- package/foundations/theme.js +42 -0
- package/foundations/tokens.css +1656 -0
- package/icons/custom/custom-video-horizontal-filled.marko +6 -0
- package/icons/custom/custom-video-horizontal-regular.marko +6 -0
- package/icons/custom/custom-video-tour-3d-filled.marko +7 -0
- package/icons/custom/custom-video-tour-3d-regular.marko +9 -0
- package/icons/custom/custom-video-vertical-filled.marko +6 -0
- package/icons/custom/custom-video-vertical-regular.marko +6 -0
- package/icons/generated/fluent-add-regular.marko +3 -0
- package/icons/generated/fluent-alert-regular.marko +3 -0
- package/icons/generated/fluent-apps-regular.marko +3 -0
- package/icons/generated/fluent-arrow-download-regular.marko +3 -0
- package/icons/generated/fluent-arrow-left-regular.marko +3 -0
- package/icons/generated/fluent-arrow-right-regular.marko +3 -0
- package/icons/generated/fluent-arrow-rotate-clockwise-regular.marko +3 -0
- package/icons/generated/fluent-arrow-rotate-counterclockwise-regular.marko +3 -0
- package/icons/generated/fluent-border-all-regular.marko +3 -0
- package/icons/generated/fluent-calendar-regular.marko +3 -0
- package/icons/generated/fluent-checkmark-circle-filled.marko +3 -0
- package/icons/generated/fluent-checkmark-regular.marko +3 -0
- package/icons/generated/fluent-chevron-down-regular.marko +3 -0
- package/icons/generated/fluent-chevron-left-regular.marko +3 -0
- package/icons/generated/fluent-chevron-right-regular.marko +3 -0
- package/icons/generated/fluent-chevron-up-regular.marko +3 -0
- package/icons/generated/fluent-circle-half-fill-regular.marko +3 -0
- package/icons/generated/fluent-clock-regular.marko +3 -0
- package/icons/generated/fluent-color-regular.marko +3 -0
- package/icons/generated/fluent-crop-regular.marko +3 -0
- package/icons/generated/fluent-dark-theme-regular.marko +3 -0
- package/icons/generated/fluent-delete-regular.marko +3 -0
- package/icons/generated/fluent-desktop-regular.marko +3 -0
- package/icons/generated/fluent-dismiss-circle-filled.marko +3 -0
- package/icons/generated/fluent-dismiss-regular.marko +3 -0
- package/icons/generated/fluent-document-pdf-regular.marko +3 -0
- package/icons/generated/fluent-document-regular.marko +3 -0
- package/icons/generated/fluent-eye-off-regular.marko +3 -0
- package/icons/generated/fluent-eye-regular.marko +3 -0
- package/icons/generated/fluent-eyedropper-regular.marko +3 -0
- package/icons/generated/fluent-grid-regular.marko +3 -0
- package/icons/generated/fluent-history-regular.marko +3 -0
- package/icons/generated/fluent-home-regular.marko +3 -0
- package/icons/generated/fluent-image-regular.marko +3 -0
- package/icons/generated/fluent-info-regular.marko +3 -0
- package/icons/generated/fluent-link-regular.marko +3 -0
- package/icons/generated/fluent-more-horizontal-regular.marko +3 -0
- package/icons/generated/fluent-navigation-regular.marko +3 -0
- package/icons/generated/fluent-paint-brush-regular.marko +3 -0
- package/icons/generated/fluent-question-circle-regular.marko +3 -0
- package/icons/generated/fluent-search-regular.marko +3 -0
- package/icons/generated/fluent-settings-regular.marko +3 -0
- package/icons/generated/fluent-square-hint-regular.marko +3 -0
- package/icons/generated/fluent-subtract-regular.marko +3 -0
- package/icons/generated/fluent-text-align-center-regular.marko +3 -0
- package/icons/generated/fluent-text-align-left-regular.marko +3 -0
- package/icons/generated/fluent-text-align-right-regular.marko +3 -0
- package/icons/generated/fluent-text-bold-regular.marko +3 -0
- package/icons/generated/fluent-text-bullet-list-regular.marko +3 -0
- package/icons/generated/fluent-text-font-regular.marko +3 -0
- package/icons/generated/fluent-text-font-size-regular.marko +3 -0
- package/icons/generated/fluent-text-italic-regular.marko +3 -0
- package/icons/generated/fluent-text-number-list-ltr-regular.marko +3 -0
- package/icons/generated/fluent-video-regular.marko +3 -0
- package/icons/generated/fluent-warning-filled.marko +3 -0
- package/icons/generated/fluent-weather-moon-regular.marko +3 -0
- package/icons/generated/fluent-weather-sunny-regular.marko +3 -0
- package/icons/generated/fluent-zoom-in-regular.marko +3 -0
- package/icons/generated/fluent-zoom-out-regular.marko +3 -0
- package/icons/manifest.json +61 -0
- package/icons/social/social-facebook-color.marko +7 -0
- package/icons/social/social-facebook.marko +6 -0
- package/icons/social/social-google-color.marko +20 -0
- package/icons/social/social-google.marko +6 -0
- package/icons/social/social-instagram-color.marko +16 -0
- package/icons/social/social-instagram.marko +6 -0
- package/icons/social/social-linkedin-color.marko +8 -0
- package/icons/social/social-linkedin.marko +8 -0
- package/icons/social/social-messenger-color.marko +15 -0
- package/icons/social/social-messenger.marko +6 -0
- package/icons/social/social-pinterest-color.marko +6 -0
- package/icons/social/social-pinterest.marko +6 -0
- package/icons/social/social-telegram-color.marko +7 -0
- package/icons/social/social-telegram.marko +6 -0
- package/icons/social/social-whatsapp-color.marko +8 -0
- package/icons/social/social-whatsapp.marko +7 -0
- package/icons/social/social-x-color.marko +7 -0
- package/icons/social/social-x.marko +6 -0
- package/icons/social/social-youtube-color.marko +7 -0
- package/icons/social/social-youtube.marko +6 -0
- package/marko.json +203 -0
- package/package.json +55 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
<!-- sds-card — ported from kdls-design-system v2.5.1 (0f3edf8) components/Card/Card.tsx.
|
|
2
|
+
|
|
3
|
+
The shell. Header, Body, Footer, Section, InfoRow, InfoPair and Collapsible are sibling
|
|
4
|
+
tags that nest inside it as ordinary children: the card has nothing to tell them, so
|
|
5
|
+
there is nothing for it to intercept.
|
|
6
|
+
|
|
7
|
+
No state of its own. KDLS's Card held useState for the collapsible section — that state
|
|
8
|
+
moved with it, into sds-card-collapsible, so a card with no collapsible section costs
|
|
9
|
+
nothing. A property listing renders twenty of these.
|
|
10
|
+
|
|
11
|
+
It is a <button> when it has a click handler, rather than a div with a handler and no
|
|
12
|
+
keyboard path. -->
|
|
13
|
+
|
|
14
|
+
import { assertCardSize } from "./card-variants.js";
|
|
15
|
+
|
|
16
|
+
$ {
|
|
17
|
+
const {
|
|
18
|
+
cover,
|
|
19
|
+
coverWidth,
|
|
20
|
+
coverHeight,
|
|
21
|
+
coverBadges,
|
|
22
|
+
horizontal = false,
|
|
23
|
+
size = "md",
|
|
24
|
+
bordered = true,
|
|
25
|
+
hoverable = false,
|
|
26
|
+
onClick,
|
|
27
|
+
clickLabel,
|
|
28
|
+
loading = false,
|
|
29
|
+
loadingRows = 3,
|
|
30
|
+
loadingTitleRows = 2,
|
|
31
|
+
class: extraClass = "",
|
|
32
|
+
} = input;
|
|
33
|
+
|
|
34
|
+
assertCardSize(size);
|
|
35
|
+
|
|
36
|
+
const renderable = (value) => (value && value.renderBody) || value;
|
|
37
|
+
const interactive = Boolean(onClick);
|
|
38
|
+
|
|
39
|
+
// The hit area has no text of its own — it is an empty box over the card — so it cannot
|
|
40
|
+
// borrow a name from its contents the way a normal button does. A control announced as
|
|
41
|
+
// just "button" tells a screen-reader user nothing about which card they are on, and in
|
|
42
|
+
// a listing of twenty cards that is twenty identical buttons. Same rule the icon button
|
|
43
|
+
// already applies.
|
|
44
|
+
if (interactive && !clickLabel) {
|
|
45
|
+
throw new Error("sds-card needs a `clickLabel` when it takes an `onClick` — the click target covers the card and has no text to be named by.");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const classes = [
|
|
49
|
+
"sds-card",
|
|
50
|
+
size === "sm" ? "sds-card--sm" : "",
|
|
51
|
+
horizontal ? "sds-card--horizontal" : "",
|
|
52
|
+
bordered ? "sds-card--bordered" : "",
|
|
53
|
+
hoverable || interactive ? "sds-card--hoverable" : "",
|
|
54
|
+
extraClass,
|
|
55
|
+
].filter(Boolean).join(" ");
|
|
56
|
+
|
|
57
|
+
// Each dimension belongs to the orientation that can honour it, which is the gate KDLS
|
|
58
|
+
// applies and the port dropped. A horizontal card's cover is a fixed-width column beside
|
|
59
|
+
// the content; a vertical one's is a fixed-height band above it. Applying both meant a
|
|
60
|
+
// `coverHeight` written for the vertical case silently squashed the horizontal one.
|
|
61
|
+
const coverStyle = [
|
|
62
|
+
horizontal && coverWidth ? `width: ${coverWidth}px` : "",
|
|
63
|
+
!horizontal && coverHeight ? `height: ${coverHeight}px` : "",
|
|
64
|
+
].filter(Boolean).join("; ") || null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
<macro|{ }| name="inner">
|
|
68
|
+
// `cover`, not `cover || loading`. A loading card with no cover drew a cover-shaped
|
|
69
|
+
// skeleton block, promising a picture that was never going to arrive — and once loaded
|
|
70
|
+
// the card jumped as that block disappeared. KDLS gates on the cover alone.
|
|
71
|
+
<if(cover)>
|
|
72
|
+
<div class="sds-card__cover" style=coverStyle>
|
|
73
|
+
<if(loading)>
|
|
74
|
+
<sds-skeleton-image fill/>
|
|
75
|
+
</if>
|
|
76
|
+
<else>
|
|
77
|
+
<${renderable(cover)}/>
|
|
78
|
+
</else>
|
|
79
|
+
<if(coverBadges && !loading)>
|
|
80
|
+
<div class="sds-card__cover-badges"><${renderable(coverBadges)}/></div>
|
|
81
|
+
</if>
|
|
82
|
+
</div>
|
|
83
|
+
</if>
|
|
84
|
+
|
|
85
|
+
<div class="sds-card__content">
|
|
86
|
+
<if(loading)>
|
|
87
|
+
// `loadingTitleRows` is a count, and it was being spent as a yes/no: 1, 2 and 10 all
|
|
88
|
+
// drew the same single title line, and the default of 2 lost its second row. The
|
|
89
|
+
// first row is the title; whatever remains are lines under it.
|
|
90
|
+
<sds-skeleton
|
|
91
|
+
title=(loadingTitleRows >= 1)
|
|
92
|
+
rows=(Math.max(0, loadingTitleRows - 1) + loadingRows)
|
|
93
|
+
label="Carregando"
|
|
94
|
+
/>
|
|
95
|
+
</if>
|
|
96
|
+
<else>
|
|
97
|
+
<${input.renderBody}/>
|
|
98
|
+
</else>
|
|
99
|
+
</div>
|
|
100
|
+
</macro>
|
|
101
|
+
|
|
102
|
+
<div class=classes aria-busy=(loading ? "true" : null)>
|
|
103
|
+
<if(interactive)>
|
|
104
|
+
// The click target covers the card and sits BESIDE the content, never around it.
|
|
105
|
+
//
|
|
106
|
+
// It used to be a <button> wrapping everything, which is invalid HTML the moment the
|
|
107
|
+
// card holds a button or a link of its own — and a card in a listing almost always
|
|
108
|
+
// does. In the browser it does not merely warn: the parser closes the outer button at
|
|
109
|
+
// the inner one and the inner control becomes the card's SIBLING. Rendered on the
|
|
110
|
+
// server and read back, a card with one "Ver imóvel" button came out as an empty card
|
|
111
|
+
// followed by a loose button. Proven in card.test.js.
|
|
112
|
+
//
|
|
113
|
+
// So the card is a plain <div> again and this covers it instead. The content is
|
|
114
|
+
// painted above the hit area, and interactive children above that, so clicking the
|
|
115
|
+
// card works everywhere except on a control that had its own answer.
|
|
116
|
+
<button
|
|
117
|
+
type="button"
|
|
118
|
+
class="sds-card__hit"
|
|
119
|
+
aria-label=clickLabel
|
|
120
|
+
on-click(onClick)
|
|
121
|
+
/>
|
|
122
|
+
</if>
|
|
123
|
+
<inner/>
|
|
124
|
+
</div>
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/* SDS Checkbox.
|
|
2
|
+
Ported from kdls-design-system v2.5.1 (0f3edf8) components/Checkbox/Checkbox.css
|
|
3
|
+
by scripts/port-css.mjs. Divergences beyond the mechanical rename are noted inline.
|
|
4
|
+
|
|
5
|
+
Rules removed with the brand families they targeted:
|
|
6
|
+
[data-color-scheme="lya"] .sds-checkbox--indeterminate .sds-checkbox__box
|
|
7
|
+
[data-color-scheme="lya"] .sds-checkbox--indeterminate:active:not(.sds-checkbox--disabled) .sds-checkbox__box
|
|
8
|
+
[data-color-scheme="lya"] .sds-checkbox:active:not(.sds-checkbox--disabled):not(.sds-checkbox--checked):not(.sds-checkbox--indeterminate) .sds-checkbox__box
|
|
9
|
+
*/.sds-checkbox {
|
|
10
|
+
display: inline-flex;
|
|
11
|
+
/* A column, because the hint is a sibling of the clickable row rather than part of it —
|
|
12
|
+
the row is `__control` and it is still a row. This was `align-items: center` on a flex
|
|
13
|
+
row, so a hint sat BESIDE the label on the same line, reading as a second label. */
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
align-items: flex-start;
|
|
16
|
+
gap: var(--spacing-1);
|
|
17
|
+
cursor: pointer;
|
|
18
|
+
user-select: none;
|
|
19
|
+
font-family: var(--font-family-base);
|
|
20
|
+
outline: none;
|
|
21
|
+
}
|
|
22
|
+
.sds-checkbox--disabled {
|
|
23
|
+
cursor: not-allowed;
|
|
24
|
+
}
|
|
25
|
+
/* Box */
|
|
26
|
+
.sds-checkbox__box {
|
|
27
|
+
flex-shrink: 0;
|
|
28
|
+
display: inline-flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
justify-content: center;
|
|
31
|
+
border: 1px solid var(--border-interactive-color);
|
|
32
|
+
border-radius: var(--radius-xs);
|
|
33
|
+
transition: background 120ms ease, border-color 120ms ease, box-shadow 120ms ease;
|
|
34
|
+
background: var(--component-bg-color);
|
|
35
|
+
}
|
|
36
|
+
/* Size: small — 24px row height */
|
|
37
|
+
.sds-checkbox--sm {
|
|
38
|
+
min-height: 24px;
|
|
39
|
+
--checkbox-box-size: 24px;
|
|
40
|
+
}
|
|
41
|
+
.sds-checkbox--sm .sds-checkbox__box {
|
|
42
|
+
width: 24px;
|
|
43
|
+
height: 24px;
|
|
44
|
+
}
|
|
45
|
+
/* Size: medium — 32px row height */
|
|
46
|
+
.sds-checkbox--md {
|
|
47
|
+
min-height: 32px;
|
|
48
|
+
--checkbox-box-size: 32px;
|
|
49
|
+
}
|
|
50
|
+
.sds-checkbox--md .sds-checkbox__box {
|
|
51
|
+
width: 32px;
|
|
52
|
+
height: 32px;
|
|
53
|
+
}
|
|
54
|
+
/* ── Checked: the input says so, not a class ──────────────────────────────────
|
|
55
|
+
`--checked` comes from the `checked` prop, which is fixed at render. The component
|
|
56
|
+
holds no state and does not hydrate, so clicking marked the native input — the browser
|
|
57
|
+
does that — and the box did not fill. `:checked` is the browser's own answer, and it is
|
|
58
|
+
right with no JavaScript at all.
|
|
59
|
+
|
|
60
|
+
The class stays alongside it, so a caller that controls `checked` gets the visual on
|
|
61
|
+
the first render, before the input is touched. */
|
|
62
|
+
/* Unchecked states */
|
|
63
|
+
.sds-checkbox:hover:not(.sds-checkbox--disabled):not(.sds-checkbox--checked):not(.sds-checkbox--indeterminate):not(:has(:checked)) .sds-checkbox__box,
|
|
64
|
+
.sds-checkbox:active:not(.sds-checkbox--disabled):not(.sds-checkbox--checked):not(.sds-checkbox--indeterminate):not(:has(:checked)) .sds-checkbox__box {
|
|
65
|
+
border-color: var(--color-primary-50-solid);
|
|
66
|
+
}
|
|
67
|
+
/* Checked states */
|
|
68
|
+
.sds-checkbox--checked .sds-checkbox__box,
|
|
69
|
+
.sds-checkbox--indeterminate .sds-checkbox__box,
|
|
70
|
+
.sds-checkbox__input:checked ~ .sds-checkbox__box {
|
|
71
|
+
background: var(--color-primary-50);
|
|
72
|
+
border-color: var(--color-primary-50-solid);
|
|
73
|
+
color: var(--color-on-primary);
|
|
74
|
+
}
|
|
75
|
+
.sds-checkbox--checked:hover:not(.sds-checkbox--disabled) .sds-checkbox__box,
|
|
76
|
+
.sds-checkbox--indeterminate:hover:not(.sds-checkbox--disabled) .sds-checkbox__box,
|
|
77
|
+
.sds-checkbox:hover:not(.sds-checkbox--disabled) .sds-checkbox__input:checked ~ .sds-checkbox__box {
|
|
78
|
+
background: var(--color-primary-60);
|
|
79
|
+
border-color: var(--color-primary-60-solid);
|
|
80
|
+
}
|
|
81
|
+
.sds-checkbox--checked:active:not(.sds-checkbox--disabled) .sds-checkbox__box,
|
|
82
|
+
.sds-checkbox--indeterminate:active:not(.sds-checkbox--disabled) .sds-checkbox__box,
|
|
83
|
+
.sds-checkbox:active:not(.sds-checkbox--disabled) .sds-checkbox__input:checked ~ .sds-checkbox__box {
|
|
84
|
+
background: var(--color-primary-60);
|
|
85
|
+
border-color: var(--color-primary-60-solid);
|
|
86
|
+
}
|
|
87
|
+
/* ── Feedback (validation) — box border when unchecked and not indeterminate ── */
|
|
88
|
+
.sds-checkbox--feedback-error:not(.sds-checkbox--checked):not(.sds-checkbox--indeterminate) .sds-checkbox__box { border-color: var(--color-error-50); }
|
|
89
|
+
.sds-checkbox--feedback-alert:not(.sds-checkbox--checked):not(.sds-checkbox--indeterminate) .sds-checkbox__box { border-color: var(--color-alert-50); }
|
|
90
|
+
.sds-checkbox--feedback-success:not(.sds-checkbox--checked):not(.sds-checkbox--indeterminate) .sds-checkbox__box { border-color: var(--color-success-50); }
|
|
91
|
+
/* ── Disabled ── */
|
|
92
|
+
.sds-checkbox--disabled .sds-checkbox__box {
|
|
93
|
+
background: var(--color-neutral-20);
|
|
94
|
+
border-color: var(--border-interactive-color);
|
|
95
|
+
}
|
|
96
|
+
.sds-checkbox--checked.sds-checkbox--disabled .sds-checkbox__box,
|
|
97
|
+
.sds-checkbox--indeterminate.sds-checkbox--disabled .sds-checkbox__box {
|
|
98
|
+
background: var(--color-neutral-30);
|
|
99
|
+
border-color: var(--border-interactive-color);
|
|
100
|
+
}
|
|
101
|
+
.sds-checkbox--disabled .sds-checkbox__label {
|
|
102
|
+
color: var(--color-neutral-40);
|
|
103
|
+
}
|
|
104
|
+
.sds-checkbox__label--required::after {
|
|
105
|
+
content: ' *';
|
|
106
|
+
color: var(--color-error-50);
|
|
107
|
+
}
|
|
108
|
+
/* Focus ring */
|
|
109
|
+
.sds-checkbox:focus-visible .sds-checkbox__box {
|
|
110
|
+
border-color: var(--color-primary-50-solid);
|
|
111
|
+
box-shadow: var(--focus-ring);
|
|
112
|
+
}
|
|
113
|
+
/* Checkmark icon */
|
|
114
|
+
.sds-checkbox__icon {
|
|
115
|
+
width: 100%;
|
|
116
|
+
height: 100%;
|
|
117
|
+
display: flex;
|
|
118
|
+
align-items: center;
|
|
119
|
+
justify-content: center;
|
|
120
|
+
opacity: 0;
|
|
121
|
+
color: var(--color-on-primary);
|
|
122
|
+
transition: opacity 120ms ease;
|
|
123
|
+
}
|
|
124
|
+
.sds-checkbox--checked .sds-checkbox__icon,
|
|
125
|
+
.sds-checkbox--indeterminate .sds-checkbox__icon,
|
|
126
|
+
.sds-checkbox__input:checked ~ .sds-checkbox__box .sds-checkbox__icon {
|
|
127
|
+
opacity: 1;
|
|
128
|
+
}
|
|
129
|
+
/* Container */
|
|
130
|
+
.sds-checkbox-container {
|
|
131
|
+
display: flex;
|
|
132
|
+
align-items: center;
|
|
133
|
+
height: 56px;
|
|
134
|
+
padding: 0 var(--spacing-4);
|
|
135
|
+
border: 1px solid var(--border-interactive-color);
|
|
136
|
+
border-radius: var(--radius-md);
|
|
137
|
+
background: var(--component-bg-color);
|
|
138
|
+
}
|
|
139
|
+
.sds-checkbox-container--disabled {
|
|
140
|
+
border-color: var(--border-static-color);
|
|
141
|
+
}
|
|
142
|
+
[data-theme="dark"] .sds-checkbox-container {
|
|
143
|
+
border-color: var(--color-neutral-60);
|
|
144
|
+
}
|
|
145
|
+
/* Hint */
|
|
146
|
+
.sds-checkbox-wrapper {
|
|
147
|
+
display: flex;
|
|
148
|
+
flex-direction: column;
|
|
149
|
+
align-items: flex-start;
|
|
150
|
+
gap: var(--spacing-1);
|
|
151
|
+
}
|
|
152
|
+
/* Width modifier */
|
|
153
|
+
.sds-checkbox-wrapper--fill { width: 100%; }
|
|
154
|
+
.sds-checkbox-wrapper--fill > .sds-checkbox-container { width: 100%; }
|
|
155
|
+
/* Container variant stretches to fill available width */
|
|
156
|
+
.sds-checkbox-wrapper > .sds-checkbox-container {
|
|
157
|
+
align-self: stretch;
|
|
158
|
+
}
|
|
159
|
+
/* Label position: left — box moves to the right */
|
|
160
|
+
.sds-checkbox--label-left {
|
|
161
|
+
flex-direction: row-reverse;
|
|
162
|
+
}
|
|
163
|
+
.sds-checkbox-container .sds-checkbox--label-left {
|
|
164
|
+
width: 100%;
|
|
165
|
+
justify-content: space-between;
|
|
166
|
+
}
|
|
167
|
+
/* Content column (label + hint stacked, no-container case) */
|
|
168
|
+
/* Divergence: the clickable row is `__control`, and `__content` is gone.
|
|
169
|
+
|
|
170
|
+
KDLS puts the box and the label straight inside the root and uses `__content` for a
|
|
171
|
+
different job — it appears only when there is a hint, and stacks the label above it.
|
|
172
|
+
This port moved the hint out of the label (a hint is not part of the thing you click),
|
|
173
|
+
so the element that needed a name was the row, and it inherited the KDLS name along
|
|
174
|
+
with the KDLS rule: `flex-direction: column`. The box sat on top of its own label in
|
|
175
|
+
every checkbox and radio in the product. Rafael found it on the portal. */
|
|
176
|
+
.sds-checkbox__control {
|
|
177
|
+
display: flex;
|
|
178
|
+
align-items: center;
|
|
179
|
+
gap: var(--spacing-2);
|
|
180
|
+
}
|
|
181
|
+
/* Hint.
|
|
182
|
+
|
|
183
|
+
Secondary by default: it explains the label, and when it reads in the same ink at the
|
|
184
|
+
same weight it competes with it instead. The template used to pass `color="inherit"`,
|
|
185
|
+
so it took the page's body colour and the feedback classes below never matched anything
|
|
186
|
+
— they were written at the port and have been dead since.
|
|
187
|
+
|
|
188
|
+
Indented past the box, so it lines up with the label rather than with the control. */
|
|
189
|
+
.sds-checkbox__hint {
|
|
190
|
+
padding-left: calc(var(--checkbox-box-size, 24px) + var(--spacing-2));
|
|
191
|
+
color: var(--color-neutral-60);
|
|
192
|
+
}
|
|
193
|
+
.sds-checkbox--label-left .sds-checkbox__hint {
|
|
194
|
+
padding-left: 0;
|
|
195
|
+
}
|
|
196
|
+
.sds-checkbox-container + .sds-checkbox__hint {
|
|
197
|
+
padding-left: var(--spacing-4);
|
|
198
|
+
}
|
|
199
|
+
.sds-checkbox__hint.sds-checkbox__hint--success { color: var(--color-success-50); }
|
|
200
|
+
.sds-checkbox__hint.sds-checkbox__hint--alert { color: var(--color-alert-50); }
|
|
201
|
+
.sds-checkbox__hint.sds-checkbox__hint--error { color: var(--color-error-50); }
|
|
202
|
+
.sds-checkbox__hint.sds-checkbox__hint--disabled {
|
|
203
|
+
color: var(--color-neutral-40);
|
|
204
|
+
}
|
|
205
|
+
[data-theme="dark"] .sds-checkbox-container--disabled {
|
|
206
|
+
border-color: var(--border-interactive-color);
|
|
207
|
+
}
|
|
208
|
+
/* ── The native input ──────────────────────────────────────────────────────────
|
|
209
|
+
SDS renders a real <input type="checkbox">, which KDLS did not: it drew a
|
|
210
|
+
<div role="checkbox"> and bolted a hidden <input> alongside so a form could still
|
|
211
|
+
submit the value. The native element brings keyboard activation, focus, the disabled
|
|
212
|
+
state, label association and form participation — all of which KDLS rebuilt by hand.
|
|
213
|
+
|
|
214
|
+
The input itself is visually hidden rather than display:none, because a hidden input
|
|
215
|
+
cannot be focused and an unfocusable checkbox cannot be reached by keyboard. The box
|
|
216
|
+
the visitor sees is .sds-checkbox__box, drawn beside it and driven by :checked. */
|
|
217
|
+
.sds-checkbox__input {
|
|
218
|
+
position: absolute;
|
|
219
|
+
width: 1px;
|
|
220
|
+
height: 1px;
|
|
221
|
+
margin: 0;
|
|
222
|
+
opacity: 0;
|
|
223
|
+
pointer-events: none;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.sds-checkbox__input:checked ~ .sds-checkbox__box,
|
|
227
|
+
.sds-checkbox--indeterminate .sds-checkbox__box {
|
|
228
|
+
background: var(--color-primary-50);
|
|
229
|
+
border-color: var(--color-primary-50-solid);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.sds-checkbox__input:focus-visible ~ .sds-checkbox__box {
|
|
233
|
+
box-shadow: var(--focus-ring);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.sds-checkbox__input:checked ~ .sds-checkbox__box .sds-checkbox__icon,
|
|
237
|
+
.sds-checkbox--indeterminate .sds-checkbox__box .sds-checkbox__icon {
|
|
238
|
+
opacity: 1;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.sds-checkbox__icon {
|
|
242
|
+
opacity: 0;
|
|
243
|
+
color: var(--color-on-primary);
|
|
244
|
+
display: inline-flex;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/* Indeterminate is drawn from a class rather than the :indeterminate pseudo-class.
|
|
248
|
+
That property can only be set from JavaScript, and this component does not hydrate —
|
|
249
|
+
with appearance:none the browser draws nothing here anyway, so the class is enough. */
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Checkbox vocabulary.
|
|
3
|
+
*
|
|
4
|
+
* Ported from kdls-design-system v2.5.1 (0f3edf8) components/Checkbox/Checkbox.tsx.
|
|
5
|
+
*/
|
|
6
|
+
export const CHECKBOX_SIZES = ['sm', 'md'];
|
|
7
|
+
export const CHECKBOX_FEEDBACKS = ['none', 'success', 'alert', 'error'];
|
|
8
|
+
export const CHECKBOX_WIDTHS = ['hug', 'fill'];
|
|
9
|
+
export const CHECKBOX_LABEL_POSITIONS = ['right', 'left'];
|
|
10
|
+
|
|
11
|
+
function assertOneOf(label, value, allowed) {
|
|
12
|
+
if (!allowed.includes(value)) {
|
|
13
|
+
throw new Error(`Unknown checkbox ${label}: "${value}". Allowed: ${allowed.join(', ')}`);
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const assertCheckboxSize = (v) => assertOneOf('size', v, CHECKBOX_SIZES);
|
|
19
|
+
export const assertLabelPosition = (v) => assertOneOf('labelPosition', v, CHECKBOX_LABEL_POSITIONS);
|
|
20
|
+
export const assertFeedback = (v) => assertOneOf('feedback', v, CHECKBOX_FEEDBACKS);
|
|
21
|
+
export const assertWidth = (v) => assertOneOf('width', v, CHECKBOX_WIDTHS);
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
<!-- sds-checkbox — ported from kdls-design-system v2.5.1 (0f3edf8)
|
|
2
|
+
components/Checkbox/Checkbox.tsx.
|
|
3
|
+
|
|
4
|
+
A real <input type="checkbox">, which KDLS's was not: it drew a <div role="checkbox"> and
|
|
5
|
+
bolted a hidden <input> alongside so a form could still submit. The native element brings
|
|
6
|
+
keyboard activation, focus, the disabled state, label association and form participation —
|
|
7
|
+
everything KDLS rebuilt by hand — and with appearance:none the box on screen is still
|
|
8
|
+
entirely ours.
|
|
9
|
+
|
|
10
|
+
It does not hydrate, and takes no onChange. The native `change` event bubbles: a form, or
|
|
11
|
+
a Select rendering a list of these, listens once on its own container instead of every
|
|
12
|
+
checkbox carrying a listener. On a page with twenty filter checkboxes that is the
|
|
13
|
+
difference between one listener and twenty serialised components.
|
|
14
|
+
|
|
15
|
+
`indeterminate` is a class, not the DOM property. That property can only be set from
|
|
16
|
+
JavaScript, and since appearance:none means the browser draws nothing here, the class is
|
|
17
|
+
what the visual comes from anyway. -->
|
|
18
|
+
|
|
19
|
+
import { assertCheckboxSize, assertFeedback, assertWidth, assertLabelPosition } from "./checkbox-variants.js";
|
|
20
|
+
|
|
21
|
+
$ {
|
|
22
|
+
const {
|
|
23
|
+
label,
|
|
24
|
+
// A checkbox in a table row needs a name and no visible text — KDLS renders those with
|
|
25
|
+
// no label at all, so a screen reader announces "checkbox" and nothing about which row.
|
|
26
|
+
hideLabel = false,
|
|
27
|
+
size = "sm",
|
|
28
|
+
width = "hug",
|
|
29
|
+
labelPosition = "right",
|
|
30
|
+
checked = false,
|
|
31
|
+
indeterminate = false,
|
|
32
|
+
disabled = false,
|
|
33
|
+
required = false,
|
|
34
|
+
container = false,
|
|
35
|
+
feedback = "none",
|
|
36
|
+
hint,
|
|
37
|
+
name,
|
|
38
|
+
value,
|
|
39
|
+
// `component.id`, not a random string. Marko gives every component an id that is the
|
|
40
|
+
// same on the server and in the browser; a random one drawn inside the render block is
|
|
41
|
+
// a different value on each side, so every `for` and `aria-*` pointing at it resolved
|
|
42
|
+
// to nothing after hydration — and changed again on every re-render.
|
|
43
|
+
id = `sds-checkbox-${component.id}`,
|
|
44
|
+
class: extraClass = "",
|
|
45
|
+
} = input;
|
|
46
|
+
|
|
47
|
+
assertLabelPosition(labelPosition);
|
|
48
|
+
|
|
49
|
+
assertCheckboxSize(size);
|
|
50
|
+
assertFeedback(feedback);
|
|
51
|
+
assertWidth(width);
|
|
52
|
+
|
|
53
|
+
const hintId = hint ? `${id}-hint` : null;
|
|
54
|
+
|
|
55
|
+
const classes = [
|
|
56
|
+
"sds-checkbox",
|
|
57
|
+
`sds-checkbox--${size}`,
|
|
58
|
+
checked && !indeterminate ? "sds-checkbox--checked" : "",
|
|
59
|
+
indeterminate ? "sds-checkbox--indeterminate" : "",
|
|
60
|
+
disabled ? "sds-checkbox--disabled" : "",
|
|
61
|
+
container ? "sds-checkbox-container" : "",
|
|
62
|
+
width === "fill" ? "sds-checkbox-wrapper--fill" : "",
|
|
63
|
+
labelPosition === "left" ? "sds-checkbox--label-left" : "",
|
|
64
|
+
feedback !== "none" ? `sds-checkbox--feedback-${feedback}` : "",
|
|
65
|
+
extraClass,
|
|
66
|
+
].filter(Boolean).join(" ");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
<div class=classes>
|
|
70
|
+
<label class="sds-checkbox__control" for=id>
|
|
71
|
+
<input
|
|
72
|
+
class="sds-checkbox__input"
|
|
73
|
+
type="checkbox"
|
|
74
|
+
id=id
|
|
75
|
+
name=name
|
|
76
|
+
value=value
|
|
77
|
+
checked=(checked || null)
|
|
78
|
+
disabled=(disabled || null)
|
|
79
|
+
required=(required || null)
|
|
80
|
+
aria-checked=(indeterminate ? "mixed" : null)
|
|
81
|
+
aria-describedby=hintId
|
|
82
|
+
// Said to a screen reader, not only drawn. The box turned red and nothing announced
|
|
83
|
+
// that the field was wrong.
|
|
84
|
+
aria-invalid=(feedback === "error" ? "true" : null)
|
|
85
|
+
/>
|
|
86
|
+
|
|
87
|
+
<span class="sds-checkbox__box" aria-hidden="true">
|
|
88
|
+
<span class="sds-checkbox__icon">
|
|
89
|
+
<if(indeterminate)>
|
|
90
|
+
<sds-icon size="xs"><fluent-subtract-regular/></sds-icon>
|
|
91
|
+
</if>
|
|
92
|
+
<else>
|
|
93
|
+
<sds-icon size="xs"><fluent-checkmark-regular/></sds-icon>
|
|
94
|
+
</else>
|
|
95
|
+
</span>
|
|
96
|
+
</span>
|
|
97
|
+
|
|
98
|
+
<if(label)>
|
|
99
|
+
<sds-text
|
|
100
|
+
as="span"
|
|
101
|
+
variant="bodyMd"
|
|
102
|
+
color="inherit"
|
|
103
|
+
class=(hideLabel ? "sds-visually-hidden" : "sds-checkbox__label")
|
|
104
|
+
>
|
|
105
|
+
${label}
|
|
106
|
+
</sds-text>
|
|
107
|
+
</if>
|
|
108
|
+
</label>
|
|
109
|
+
|
|
110
|
+
<if(hint)>
|
|
111
|
+
// The feedback modifier is what colours it, and it was never written: the hint carried
|
|
112
|
+
// `color="inherit"` and took the page's ink, so an error message read like an ordinary
|
|
113
|
+
// note. The rules for `--error`, `--alert`, `--success` and `--disabled` have been in
|
|
114
|
+
// the stylesheet since the port with nothing to match. Same fault the Input had.
|
|
115
|
+
<sds-text
|
|
116
|
+
as="span"
|
|
117
|
+
id=hintId
|
|
118
|
+
variant="labelRegular"
|
|
119
|
+
// No `color="inherit"`: that writes `sds-text--inherit`, which reads `color: inherit`
|
|
120
|
+
// and beats the hint's own rule on equal specificity by coming later in the sheet.
|
|
121
|
+
// The hint's colour is the hint's to decide — secondary by default, and the feedback
|
|
122
|
+
// colour when there is one.
|
|
123
|
+
class=`sds-checkbox__hint sds-checkbox__hint--${disabled ? "disabled" : feedback}`
|
|
124
|
+
>
|
|
125
|
+
${hint}
|
|
126
|
+
</sds-text>
|
|
127
|
+
</if>
|
|
128
|
+
</div>
|