@ds-mo/ui 0.2.4 → 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.
- package/dist/components/ds-accordion.d.ts +11 -0
- package/dist/components/ds-accordion.js +2 -0
- package/dist/components/ds-accordion.js.map +1 -0
- package/dist/components/ds-badge.d.ts +11 -0
- package/dist/components/ds-badge.js +2 -0
- package/dist/components/ds-badge.js.map +1 -0
- package/dist/components/ds-banner.d.ts +11 -0
- package/dist/components/ds-banner.js +2 -0
- package/dist/components/ds-banner.js.map +1 -0
- package/dist/components/ds-breadcrumb.d.ts +11 -0
- package/dist/components/ds-breadcrumb.js +2 -0
- package/dist/components/ds-breadcrumb.js.map +1 -0
- package/dist/components/ds-button.d.ts +11 -0
- package/dist/components/ds-button.js +2 -0
- package/dist/components/ds-button.js.map +1 -0
- package/dist/components/ds-card.d.ts +11 -0
- package/dist/components/ds-card.js +2 -0
- package/dist/components/ds-card.js.map +1 -0
- package/dist/components/ds-checkbox.d.ts +11 -0
- package/dist/components/ds-checkbox.js +2 -0
- package/dist/components/ds-checkbox.js.map +1 -0
- package/dist/components/ds-divider.d.ts +11 -0
- package/dist/components/ds-divider.js +2 -0
- package/dist/components/ds-divider.js.map +1 -0
- package/dist/components/ds-empty-state.d.ts +11 -0
- package/dist/components/ds-empty-state.js +2 -0
- package/dist/components/ds-empty-state.js.map +1 -0
- package/dist/components/ds-fade.d.ts +11 -0
- package/dist/components/ds-fade.js +2 -0
- package/dist/components/ds-fade.js.map +1 -0
- package/dist/components/ds-field.d.ts +11 -0
- package/dist/components/ds-field.js +2 -0
- package/dist/components/ds-field.js.map +1 -0
- package/dist/components/ds-header.d.ts +11 -0
- package/dist/components/ds-header.js +2 -0
- package/dist/components/ds-header.js.map +1 -0
- package/dist/components/ds-input.d.ts +11 -0
- package/dist/components/ds-input.js +2 -0
- package/dist/components/ds-input.js.map +1 -0
- package/dist/components/ds-loader.d.ts +11 -0
- package/dist/components/ds-loader.js +2 -0
- package/dist/components/ds-loader.js.map +1 -0
- package/dist/components/ds-menu.d.ts +11 -0
- package/dist/components/ds-menu.js +2 -0
- package/dist/components/ds-menu.js.map +1 -0
- package/dist/components/ds-modal.d.ts +11 -0
- package/dist/components/ds-modal.js +2 -0
- package/dist/components/ds-modal.js.map +1 -0
- package/dist/components/ds-pagination.d.ts +11 -0
- package/dist/components/ds-pagination.js +2 -0
- package/dist/components/ds-pagination.js.map +1 -0
- package/dist/components/ds-radio-group.d.ts +11 -0
- package/dist/components/ds-radio-group.js +2 -0
- package/dist/components/ds-radio-group.js.map +1 -0
- package/dist/components/ds-scrollbar.d.ts +11 -0
- package/dist/components/ds-scrollbar.js +2 -0
- package/dist/components/ds-scrollbar.js.map +1 -0
- package/dist/components/ds-sidebar.d.ts +11 -0
- package/dist/components/ds-sidebar.js +2 -0
- package/dist/components/ds-sidebar.js.map +1 -0
- package/dist/components/ds-skeleton.d.ts +11 -0
- package/dist/components/ds-skeleton.js +2 -0
- package/dist/components/ds-skeleton.js.map +1 -0
- package/dist/components/ds-slider.d.ts +11 -0
- package/dist/components/ds-slider.js +2 -0
- package/dist/components/ds-slider.js.map +1 -0
- package/dist/components/ds-surface.d.ts +11 -0
- package/dist/components/ds-surface.js +2 -0
- package/dist/components/ds-surface.js.map +1 -0
- package/dist/components/ds-tab-group.d.ts +11 -0
- package/dist/components/ds-tab-group.js +2 -0
- package/dist/components/ds-tab-group.js.map +1 -0
- package/dist/components/ds-table.d.ts +11 -0
- package/dist/components/ds-table.js +2 -0
- package/dist/components/ds-table.js.map +1 -0
- package/dist/components/ds-tag.d.ts +11 -0
- package/dist/components/ds-tag.js +2 -0
- package/dist/components/ds-tag.js.map +1 -0
- package/dist/components/ds-text.d.ts +11 -0
- package/dist/components/ds-text.js +2 -0
- package/dist/components/ds-text.js.map +1 -0
- package/dist/components/ds-toast-provider.d.ts +11 -0
- package/dist/components/ds-toast-provider.js +2 -0
- package/dist/components/ds-toast-provider.js.map +1 -0
- package/dist/components/ds-toggle.d.ts +11 -0
- package/dist/components/ds-toggle.js +2 -0
- package/dist/components/ds-toggle.js.map +1 -0
- package/dist/components/ds-tooltip.d.ts +11 -0
- package/dist/components/ds-tooltip.js +2 -0
- package/dist/components/ds-tooltip.js.map +1 -0
- package/dist/components/index.d.ts +33 -0
- package/dist/components/index.js +2 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/p-BcuUR5Ao.js +2 -0
- package/dist/components/p-BcuUR5Ao.js.map +1 -0
- package/dist/types/.stencil/Accordion/Accordion.d.ts +28 -0
- package/dist/types/.stencil/Badge/Badge.d.ts +8 -0
- package/dist/types/.stencil/Badge/index.d.ts +2 -0
- package/dist/types/.stencil/Banner/Banner.d.ts +29 -0
- package/dist/types/.stencil/Breadcrumb/Breadcrumb.d.ts +16 -0
- package/dist/types/.stencil/Button/Button.d.ts +38 -0
- package/dist/types/.stencil/Button/index.d.ts +2 -0
- package/dist/types/.stencil/Card/Card.d.ts +8 -0
- package/dist/types/.stencil/Checkbox/Checkbox.d.ts +13 -0
- package/dist/types/.stencil/Checkbox/index.d.ts +2 -0
- package/dist/types/.stencil/Divider/Divider.d.ts +7 -0
- package/dist/types/.stencil/Divider/index.d.ts +2 -0
- package/dist/types/.stencil/EmptyState/EmptyState.d.ts +7 -0
- package/dist/types/.stencil/Fade/Fade.d.ts +10 -0
- package/dist/types/.stencil/Fade/index.d.ts +2 -0
- package/dist/types/.stencil/Field/Field.d.ts +8 -0
- package/dist/types/.stencil/Header/Header.d.ts +7 -0
- package/dist/types/.stencil/Input/Input.d.ts +26 -0
- package/dist/types/.stencil/Input/index.d.ts +2 -0
- package/dist/types/.stencil/Loader/Loader.d.ts +12 -0
- package/dist/types/.stencil/Loader/index.d.ts +2 -0
- package/dist/types/.stencil/Menu/Menu.d.ts +60 -0
- package/dist/types/.stencil/Modal/Modal.d.ts +31 -0
- package/dist/types/.stencil/Pagination/Pagination.d.ts +11 -0
- package/dist/types/.stencil/RadioGroup/RadioGroup.d.ts +21 -0
- package/dist/types/.stencil/RadioGroup/index.d.ts +2 -0
- package/dist/types/.stencil/Scrollbar/Scrollbar.d.ts +27 -0
- package/dist/types/.stencil/Sidebar/Sidebar.d.ts +29 -0
- package/dist/types/.stencil/Skeleton/Skeleton.d.ts +16 -0
- package/dist/types/.stencil/Skeleton/index.d.ts +2 -0
- package/dist/types/.stencil/Slider/Slider.d.ts +18 -0
- package/dist/types/.stencil/Slider/index.d.ts +2 -0
- package/dist/types/.stencil/Surface/Surface.d.ts +22 -0
- package/dist/types/.stencil/TabGroup/TabGroup.d.ts +29 -0
- package/dist/types/.stencil/Table/Table.d.ts +50 -0
- package/dist/types/.stencil/Tag/Tag.d.ts +32 -0
- package/dist/types/.stencil/Tag/index.d.ts +2 -0
- package/dist/types/.stencil/Text/Text.d.ts +22 -0
- package/dist/types/.stencil/Text/index.d.ts +2 -0
- package/dist/types/.stencil/Toast/Toast.d.ts +15 -0
- package/dist/types/.stencil/Toast/toast-service.d.ts +24 -0
- package/dist/types/.stencil/Toggle/Toggle.d.ts +10 -0
- package/dist/types/.stencil/Toggle/index.d.ts +2 -0
- package/dist/types/.stencil/Tooltip/Tooltip.d.ts +36 -0
- package/dist/types/components.d.ts +2024 -0
- package/dist/types/stencil-public-runtime.d.ts +1860 -0
- package/package.json +33 -29
- package/src/angular/angular-component-lib/utils.ts +65 -0
- package/src/angular/index.ts +35 -0
- package/src/angular/proxies.ts +819 -0
- package/dist/index.css +0 -2
- package/dist/index.d.ts +0 -969
- package/dist/index.js +0 -2492
- package/dist/index.js.map +0 -1
- package/dist/r/accordion.json +0 -79
- package/dist/r/badge.json +0 -69
- package/dist/r/banner.json +0 -90
- package/dist/r/breadcrumb.json +0 -70
- package/dist/r/button-group.json +0 -65
- package/dist/r/button.json +0 -137
- package/dist/r/card.json +0 -82
- package/dist/r/checkbox.json +0 -91
- package/dist/r/divider.json +0 -57
- package/dist/r/empty-state.json +0 -69
- package/dist/r/error-boundary.json +0 -66
- package/dist/r/fade.json +0 -65
- package/dist/r/field.json +0 -71
- package/dist/r/header.json +0 -67
- package/dist/r/input.json +0 -77
- package/dist/r/loader.json +0 -54
- package/dist/r/menu.json +0 -130
- package/dist/r/modal.json +0 -93
- package/dist/r/pagination.json +0 -80
- package/dist/r/radio.json +0 -85
- package/dist/r/registry.json +0 -1600
- package/dist/r/scrollbar.json +0 -68
- package/dist/r/select.json +0 -93
- package/dist/r/sidebar.json +0 -105
- package/dist/r/skeleton.json +0 -72
- package/dist/r/slider.json +0 -87
- package/dist/r/surface.json +0 -97
- package/dist/r/tab-group.json +0 -72
- package/dist/r/tab.json +0 -71
- package/dist/r/table.json +0 -110
- package/dist/r/tag.json +0 -110
- package/dist/r/text.json +0 -94
- package/dist/r/toast.json +0 -77
- package/dist/r/toggle-button-group.json +0 -65
- package/dist/r/toggle-button.json +0 -94
- package/dist/r/toggle.json +0 -65
- package/dist/r/tooltip.json +0 -86
- package/src/components/Accordion/Accordion.module.css +0 -66
- package/src/components/Accordion/Accordion.stories.tsx +0 -95
- package/src/components/Accordion/Accordion.tsx +0 -122
- package/src/components/Accordion/index.ts +0 -2
- package/src/components/Badge/Badge.module.css +0 -6
- package/src/components/Badge/Badge.stories.tsx +0 -99
- package/src/components/Badge/Badge.tsx +0 -23
- package/src/components/Badge/index.ts +0 -2
- package/src/components/Banner/Banner.module.css +0 -67
- package/src/components/Banner/Banner.stories.tsx +0 -81
- package/src/components/Banner/Banner.tsx +0 -109
- package/src/components/Banner/index.ts +0 -2
- package/src/components/Breadcrumb/Breadcrumb.module.css +0 -46
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +0 -72
- package/src/components/Breadcrumb/Breadcrumb.tsx +0 -68
- package/src/components/Breadcrumb/index.ts +0 -2
- package/src/components/Button/Button.mdx +0 -143
- package/src/components/Button/Button.module.css +0 -343
- package/src/components/Button/Button.stories.tsx +0 -575
- package/src/components/Button/Button.tsx +0 -201
- package/src/components/Button/index.ts +0 -10
- package/src/components/ButtonGroup/ButtonGroup.module.css +0 -157
- package/src/components/ButtonGroup/ButtonGroup.stories.tsx +0 -384
- package/src/components/ButtonGroup/ButtonGroup.tsx +0 -122
- package/src/components/ButtonGroup/index.ts +0 -2
- package/src/components/Card/Card.module.css +0 -36
- package/src/components/Card/Card.stories.tsx +0 -78
- package/src/components/Card/Card.tsx +0 -53
- package/src/components/Card/index.ts +0 -2
- package/src/components/Checkbox/Checkbox.module.css +0 -47
- package/src/components/Checkbox/Checkbox.stories.tsx +0 -129
- package/src/components/Checkbox/Checkbox.tsx +0 -68
- package/src/components/Checkbox/index.ts +0 -2
- package/src/components/Divider/Divider.module.css +0 -19
- package/src/components/Divider/Divider.stories.tsx +0 -55
- package/src/components/Divider/Divider.tsx +0 -28
- package/src/components/Divider/index.ts +0 -2
- package/src/components/EmptyState/EmptyState.module.css +0 -8
- package/src/components/EmptyState/EmptyState.stories.tsx +0 -53
- package/src/components/EmptyState/EmptyState.tsx +0 -31
- package/src/components/EmptyState/index.ts +0 -2
- package/src/components/ErrorBoundary/ErrorBoundary.module.css +0 -18
- package/src/components/ErrorBoundary/ErrorBoundary.stories.tsx +0 -38
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +0 -50
- package/src/components/ErrorBoundary/index.ts +0 -2
- package/src/components/Fade/Fade.module.css +0 -31
- package/src/components/Fade/Fade.stories.tsx +0 -35
- package/src/components/Fade/Fade.tsx +0 -33
- package/src/components/Fade/index.ts +0 -2
- package/src/components/Field/Field.module.css +0 -5
- package/src/components/Field/Field.stories.tsx +0 -111
- package/src/components/Field/Field.tsx +0 -22
- package/src/components/Field/index.ts +0 -2
- package/src/components/Header/Header.module.css +0 -33
- package/src/components/Header/Header.stories.tsx +0 -52
- package/src/components/Header/Header.tsx +0 -42
- package/src/components/Header/index.ts +0 -2
- package/src/components/Input/Input.module.css +0 -71
- package/src/components/Input/Input.stories.tsx +0 -135
- package/src/components/Input/Input.tsx +0 -76
- package/src/components/Input/index.ts +0 -2
- package/src/components/LabelWrap/LabelWrap.module.css +0 -34
- package/src/components/LabelWrap/LabelWrap.tsx +0 -32
- package/src/components/LabelWrap/index.ts +0 -1
- package/src/components/Loader/Loader.module.css +0 -9
- package/src/components/Loader/Loader.stories.tsx +0 -44
- package/src/components/Loader/Loader.tsx +0 -43
- package/src/components/Loader/index.ts +0 -2
- package/src/components/Menu/DestructiveMenuItem.module.css +0 -57
- package/src/components/Menu/DestructiveMenuItem.tsx +0 -64
- package/src/components/Menu/Menu.module.css +0 -49
- package/src/components/Menu/Menu.stories.tsx +0 -129
- package/src/components/Menu/Menu.tsx +0 -269
- package/src/components/Menu/MenuItem.module.css +0 -84
- package/src/components/Menu/MenuItem.tsx +0 -101
- package/src/components/Menu/index.ts +0 -6
- package/src/components/Modal/Modal.module.css +0 -67
- package/src/components/Modal/Modal.stories.tsx +0 -71
- package/src/components/Modal/Modal.tsx +0 -122
- package/src/components/Modal/index.ts +0 -2
- package/src/components/Pagination/Pagination.module.css +0 -64
- package/src/components/Pagination/Pagination.stories.tsx +0 -51
- package/src/components/Pagination/Pagination.tsx +0 -103
- package/src/components/Pagination/index.ts +0 -2
- package/src/components/Radio/Radio.module.css +0 -61
- package/src/components/Radio/Radio.stories.tsx +0 -101
- package/src/components/Radio/Radio.tsx +0 -108
- package/src/components/Radio/index.ts +0 -2
- package/src/components/Scrollbar/Scrollbar.module.css +0 -81
- package/src/components/Scrollbar/Scrollbar.stories.tsx +0 -46
- package/src/components/Scrollbar/Scrollbar.tsx +0 -187
- package/src/components/Scrollbar/index.ts +0 -2
- package/src/components/Select/Select.module.css +0 -31
- package/src/components/Select/Select.stories.tsx +0 -59
- package/src/components/Select/Select.tsx +0 -92
- package/src/components/Select/index.ts +0 -2
- package/src/components/Sidebar/Sidebar.module.css +0 -161
- package/src/components/Sidebar/Sidebar.stories.tsx +0 -125
- package/src/components/Sidebar/Sidebar.tsx +0 -129
- package/src/components/Sidebar/SidebarItem.tsx +0 -57
- package/src/components/Sidebar/SidebarSection.tsx +0 -42
- package/src/components/Sidebar/index.ts +0 -6
- package/src/components/Skeleton/Skeleton.module.css +0 -53
- package/src/components/Skeleton/Skeleton.stories.tsx +0 -89
- package/src/components/Skeleton/Skeleton.tsx +0 -66
- package/src/components/Skeleton/index.ts +0 -2
- package/src/components/Slider/Slider.module.css +0 -62
- package/src/components/Slider/Slider.stories.tsx +0 -120
- package/src/components/Slider/Slider.tsx +0 -74
- package/src/components/Slider/index.ts +0 -2
- package/src/components/Surface/Surface.module.css +0 -222
- package/src/components/Surface/Surface.stories.tsx +0 -213
- package/src/components/Surface/Surface.tsx +0 -219
- package/src/components/Surface/index.ts +0 -12
- package/src/components/Tab/Tab.module.css +0 -66
- package/src/components/Tab/Tab.stories.tsx +0 -103
- package/src/components/Tab/Tab.tsx +0 -41
- package/src/components/Tab/index.ts +0 -2
- package/src/components/TabGroup/TabGroup.module.css +0 -26
- package/src/components/TabGroup/TabGroup.stories.tsx +0 -60
- package/src/components/TabGroup/TabGroup.tsx +0 -83
- package/src/components/TabGroup/index.ts +0 -2
- package/src/components/Table/Table.module.css +0 -130
- package/src/components/Table/Table.stories.tsx +0 -111
- package/src/components/Table/Table.tsx +0 -226
- package/src/components/Table/index.ts +0 -11
- package/src/components/Tag/Tag.module.css +0 -270
- package/src/components/Tag/Tag.stories.tsx +0 -399
- package/src/components/Tag/Tag.tsx +0 -162
- package/src/components/Tag/index.ts +0 -2
- package/src/components/Text/Text.module.css +0 -166
- package/src/components/Text/Text.stories.tsx +0 -212
- package/src/components/Text/Text.tsx +0 -181
- package/src/components/Text/index.ts +0 -13
- package/src/components/Toast/Toast.module.css +0 -91
- package/src/components/Toast/Toast.stories.tsx +0 -66
- package/src/components/Toast/Toast.tsx +0 -182
- package/src/components/Toast/index.ts +0 -8
- package/src/components/Toggle/Toggle.module.css +0 -87
- package/src/components/Toggle/Toggle.stories.tsx +0 -85
- package/src/components/Toggle/Toggle.tsx +0 -52
- package/src/components/Toggle/index.ts +0 -2
- package/src/components/ToggleButton/ToggleButton.module.css +0 -172
- package/src/components/ToggleButton/ToggleButton.stories.tsx +0 -299
- package/src/components/ToggleButton/ToggleButton.tsx +0 -118
- package/src/components/ToggleButton/index.ts +0 -2
- package/src/components/ToggleButtonGroup/ToggleButtonGroup.module.css +0 -163
- package/src/components/ToggleButtonGroup/ToggleButtonGroup.stories.tsx +0 -341
- package/src/components/ToggleButtonGroup/ToggleButtonGroup.tsx +0 -111
- package/src/components/ToggleButtonGroup/index.ts +0 -2
- package/src/components/Tooltip/Tooltip.module.css +0 -45
- package/src/components/Tooltip/Tooltip.stories.tsx +0 -46
- package/src/components/Tooltip/Tooltip.tsx +0 -224
- package/src/components/Tooltip/index.ts +0 -2
- package/src/css-modules.d.ts +0 -4
- package/src/docs/ColorUsage.mdx +0 -195
- package/src/docs/ElevationUsage.mdx +0 -95
- package/src/docs/Introduction.mdx +0 -97
- package/src/docs/OpticalSizing.mdx +0 -178
- package/src/docs/TypographyUsage.mdx +0 -121
- package/src/index.ts +0 -154
- package/src/stories/Colors.stories.tsx +0 -528
- package/src/stories/Dimensions.stories.tsx +0 -200
- package/src/stories/Effects.stories.tsx +0 -231
- package/src/stories/Icons.stories.tsx +0 -159
- package/src/stories/Typography.stories.tsx +0 -157
- package/src/types/icons.ts +0 -15
- package/src/types/index.ts +0 -1
- package/src/utils/cn.ts +0 -6
- package/src/utils/css-tokens.ts +0 -22
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { cn } from '@/utils/cn';
|
|
3
|
-
import styles from './ToggleButtonGroup.module.css';
|
|
4
|
-
|
|
5
|
-
export interface ToggleButtonGroupProps {
|
|
6
|
-
children: React.ReactNode;
|
|
7
|
-
className?: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Renders ToggleButton children flush side-by-side with a 1px divider between them.
|
|
12
|
-
* Outer corners keep the button's own border-radius; inner corners are flattened.
|
|
13
|
-
*
|
|
14
|
-
* Individual button chrome (shadow/border) is suppressed — the group container
|
|
15
|
-
* carries the elevation so it wraps the whole control as one visual unit.
|
|
16
|
-
*
|
|
17
|
-
* Divider behaviour by elevation:
|
|
18
|
-
* elevated / flat / floating — full-height color-border-tertiary line
|
|
19
|
-
* none (ghost) — short centred line (icon/text height)
|
|
20
|
-
*
|
|
21
|
-
* Rounded groups: outer edges keep pill padding; inner edges revert to base padding.
|
|
22
|
-
* Pressed state and onPressedChange are managed by the consumer on each ToggleButton.
|
|
23
|
-
*/
|
|
24
|
-
export const ToggleButtonGroup = ({ children, className }: ToggleButtonGroupProps) => {
|
|
25
|
-
const items = React.Children.toArray(children).filter(React.isValidElement);
|
|
26
|
-
const count = items.length;
|
|
27
|
-
|
|
28
|
-
// Read elevation + rounded from first child to drive the group container style.
|
|
29
|
-
const firstEl = items[0] as React.ReactElement<Record<string, unknown>> | undefined;
|
|
30
|
-
const firstProps = firstEl?.props as { elevation?: string; rounded?: boolean } | undefined;
|
|
31
|
-
const groupElevation = firstProps?.elevation ?? 'elevated';
|
|
32
|
-
const groupRounded = !!firstProps?.rounded;
|
|
33
|
-
const groupElevKey = groupElevation.charAt(0).toUpperCase() + groupElevation.slice(1);
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<div
|
|
37
|
-
className={cn(
|
|
38
|
-
styles.group,
|
|
39
|
-
(styles as Record<string, string>)[`group${groupElevKey}`],
|
|
40
|
-
groupRounded && styles.groupRounded,
|
|
41
|
-
className,
|
|
42
|
-
)}
|
|
43
|
-
role="group"
|
|
44
|
-
>
|
|
45
|
-
{items.map((child, i) => {
|
|
46
|
-
const el = child as React.ReactElement<Record<string, unknown>>;
|
|
47
|
-
const p = el.props as {
|
|
48
|
-
className?: string;
|
|
49
|
-
elevation?: string;
|
|
50
|
-
size?: string;
|
|
51
|
-
rounded?: boolean;
|
|
52
|
-
pressed?: boolean;
|
|
53
|
-
icon?: unknown;
|
|
54
|
-
label?: unknown;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const isFirst = i === 0;
|
|
58
|
-
const isLast = i === count - 1;
|
|
59
|
-
const isMid = !isFirst && !isLast;
|
|
60
|
-
|
|
61
|
-
const size = (p.size ?? 'md') as 'xs' | 'sm' | 'md';
|
|
62
|
-
const isRounded = !!p.rounded;
|
|
63
|
-
const isPressed = !!p.pressed;
|
|
64
|
-
const isIconOnly = !!p.icon && !p.label;
|
|
65
|
-
// Ghost: elevation='none' (no chrome) → short centred divider
|
|
66
|
-
const isGhost = (p.elevation ?? 'elevated') === 'none';
|
|
67
|
-
|
|
68
|
-
// XS rounded has no extra padding in ToggleButton so needs no correction
|
|
69
|
-
const sizeKey = size.toUpperCase() as 'XS' | 'SM' | 'MD';
|
|
70
|
-
const needsRoundedCorrection = isRounded && !isIconOnly && size !== 'xs';
|
|
71
|
-
|
|
72
|
-
// Divider injected as a separate element so it's never affected by
|
|
73
|
-
// the adjacent button's opacity (e.g. disabled state).
|
|
74
|
-
const dividerClasses = cn(
|
|
75
|
-
styles.divider,
|
|
76
|
-
isGhost && styles.dividerGhost,
|
|
77
|
-
isGhost && size === 'xs' && styles.dividerGhostXS,
|
|
78
|
-
isGhost && size === 'sm' && styles.dividerGhostSM,
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
return (
|
|
82
|
-
<React.Fragment key={i}>
|
|
83
|
-
{i > 0 && <div className={dividerClasses} aria-hidden="true" />}
|
|
84
|
-
{React.cloneElement(el, {
|
|
85
|
-
className: cn(
|
|
86
|
-
p.className,
|
|
87
|
-
styles.item,
|
|
88
|
-
isFirst && styles.first,
|
|
89
|
-
isLast && styles.last,
|
|
90
|
-
isMid && styles.middle,
|
|
91
|
-
|
|
92
|
-
// Ghost variant: ::after overlay gets 4px radius treatment
|
|
93
|
-
isGhost && styles.itemShort,
|
|
94
|
-
|
|
95
|
-
// Ghost + pressed: selected state background chip
|
|
96
|
-
isGhost && isPressed && styles.itemPressed,
|
|
97
|
-
|
|
98
|
-
// Rounded: revert inner-edge padding to base (non-pill) value
|
|
99
|
-
needsRoundedCorrection && isFirst && (styles as Record<string, string>)[`roundedFirst${sizeKey}`],
|
|
100
|
-
needsRoundedCorrection && isLast && (styles as Record<string, string>)[`roundedLast${sizeKey}`],
|
|
101
|
-
needsRoundedCorrection && isMid && (styles as Record<string, string>)[`roundedMid${sizeKey}`],
|
|
102
|
-
),
|
|
103
|
-
})}
|
|
104
|
-
</React.Fragment>
|
|
105
|
-
);
|
|
106
|
-
})}
|
|
107
|
-
</div>
|
|
108
|
-
);
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
ToggleButtonGroup.displayName = 'ToggleButtonGroup';
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
.tooltip {
|
|
2
|
-
box-sizing: border-box;
|
|
3
|
-
background-color: var(--color-background-primary);
|
|
4
|
-
border-radius: var(--dimension-radius-075);
|
|
5
|
-
box-shadow: var(--effect-elevation-elevated-floating);
|
|
6
|
-
padding: var(--dimension-space-050);
|
|
7
|
-
line-height: var(--typography-lineheight-sm);
|
|
8
|
-
pointer-events: none;
|
|
9
|
-
backface-visibility: hidden;
|
|
10
|
-
-webkit-font-smoothing: subpixel-antialiased;
|
|
11
|
-
animation: tooltipFadeIn var(--effect-motion-short-2) forwards;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
.tooltipInner {
|
|
15
|
-
display: flex;
|
|
16
|
-
flex-direction: row;
|
|
17
|
-
align-items: center;
|
|
18
|
-
gap: var(--dimension-space-050);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
.keyHint {
|
|
22
|
-
width: var(--dimension-size-200);
|
|
23
|
-
height: var(--dimension-size-200);
|
|
24
|
-
flex-shrink: 0;
|
|
25
|
-
display: flex;
|
|
26
|
-
align-items: center;
|
|
27
|
-
justify-content: center;
|
|
28
|
-
border: var(--dimension-stroke-width-015) solid var(--color-foreground-tertiary);
|
|
29
|
-
border-bottom-width: var(--dimension-stroke-width-025);
|
|
30
|
-
border-radius: var(--dimension-radius-025);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.tooltip.closing {
|
|
34
|
-
animation: tooltipFadeOut var(--effect-motion-short-3) forwards;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
@keyframes tooltipFadeIn {
|
|
38
|
-
from { opacity: 0; }
|
|
39
|
-
to { opacity: 1; }
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
@keyframes tooltipFadeOut {
|
|
43
|
-
from { opacity: 1; }
|
|
44
|
-
to { opacity: 0; }
|
|
45
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
-
import { Tooltip } from './Tooltip';
|
|
3
|
-
import { Button } from '../Button';
|
|
4
|
-
|
|
5
|
-
const meta: Meta<typeof Tooltip> = {
|
|
6
|
-
title: 'Components/Tooltip',
|
|
7
|
-
component: Tooltip,
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export default meta;
|
|
11
|
-
type Story = StoryObj<typeof Tooltip>;
|
|
12
|
-
|
|
13
|
-
export const Default: Story = {
|
|
14
|
-
render: () => (
|
|
15
|
-
<div style={{ padding: 80, display: 'flex', justifyContent: 'center' }}>
|
|
16
|
-
<Tooltip label="This is a tooltip">
|
|
17
|
-
<Button variant="secondary" label="Hover me" />
|
|
18
|
-
</Tooltip>
|
|
19
|
-
</div>
|
|
20
|
-
),
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export const Positions: Story = {
|
|
24
|
-
render: () => (
|
|
25
|
-
<div style={{ padding: 100, display: 'flex', gap: 24, justifyContent: 'center' }}>
|
|
26
|
-
{(['top', 'right', 'bottom', 'left'] as const).map(side => (
|
|
27
|
-
<Tooltip key={side} label={`Tooltip on ${side}`} side={side}>
|
|
28
|
-
<Button variant="secondary" label={side} />
|
|
29
|
-
</Tooltip>
|
|
30
|
-
))}
|
|
31
|
-
</div>
|
|
32
|
-
),
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export const WithShortcut: Story = {
|
|
36
|
-
render: () => (
|
|
37
|
-
<div style={{ padding: 80, display: 'flex', gap: 24, justifyContent: 'center' }}>
|
|
38
|
-
<Tooltip label="Save" shortcutKey="S">
|
|
39
|
-
<Button variant="secondary" label="Save" />
|
|
40
|
-
</Tooltip>
|
|
41
|
-
<Tooltip label="Undo" shortcutKey="Z" shortcutKeyPosition="start">
|
|
42
|
-
<Button variant="secondary" label="Undo" />
|
|
43
|
-
</Tooltip>
|
|
44
|
-
</div>
|
|
45
|
-
),
|
|
46
|
-
};
|
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import React, { useState, useRef, useEffect, useLayoutEffect, useCallback } from 'react';
|
|
2
|
-
import { createPortal } from 'react-dom';
|
|
3
|
-
import { getCssTimeMs } from '@/utils/css-tokens';
|
|
4
|
-
import { Text } from '@/components/Text';
|
|
5
|
-
import styles from './Tooltip.module.css';
|
|
6
|
-
|
|
7
|
-
const HOVER_DELAY_FALLBACK_MS = 1000;
|
|
8
|
-
const INSTANT_REOPEN_MS = 300;
|
|
9
|
-
const FADE_OUT_DURATION_MS = 200;
|
|
10
|
-
|
|
11
|
-
let lastTooltipDismissedAt = 0;
|
|
12
|
-
|
|
13
|
-
export type TooltipSide = 'top' | 'right' | 'bottom' | 'left';
|
|
14
|
-
export type TooltipAlign = 'start' | 'center' | 'end';
|
|
15
|
-
|
|
16
|
-
export interface TooltipProps {
|
|
17
|
-
label: string;
|
|
18
|
-
children: React.ReactElement;
|
|
19
|
-
delay?: number;
|
|
20
|
-
side?: TooltipSide;
|
|
21
|
-
align?: TooltipAlign;
|
|
22
|
-
sideOffset?: number;
|
|
23
|
-
alignOffset?: number;
|
|
24
|
-
shortcutKey?: string;
|
|
25
|
-
shortcutKeyPosition?: 'start' | 'end';
|
|
26
|
-
usePortal?: boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export const Tooltip: React.FC<TooltipProps> = ({
|
|
30
|
-
label,
|
|
31
|
-
children,
|
|
32
|
-
delay,
|
|
33
|
-
side = 'top',
|
|
34
|
-
align = 'center',
|
|
35
|
-
sideOffset = 4,
|
|
36
|
-
alignOffset = 0,
|
|
37
|
-
shortcutKey,
|
|
38
|
-
shortcutKeyPosition = 'end',
|
|
39
|
-
usePortal = true,
|
|
40
|
-
}) => {
|
|
41
|
-
const hoverDelayMs = getCssTimeMs('--effect-animation-delay-medium-3', HOVER_DELAY_FALLBACK_MS);
|
|
42
|
-
const resolvedDelay = delay ?? hoverDelayMs;
|
|
43
|
-
const anchorRef = useRef<HTMLElement | null>(null);
|
|
44
|
-
const tooltipRef = useRef<HTMLDivElement>(null);
|
|
45
|
-
|
|
46
|
-
const setAnchorRef = useCallback(
|
|
47
|
-
(el: HTMLElement | null) => {
|
|
48
|
-
(anchorRef as React.MutableRefObject<HTMLElement | null>).current = el;
|
|
49
|
-
const childEl = React.Children.only(children) as React.ReactElement<{ ref?: React.Ref<unknown> }>;
|
|
50
|
-
const childRef = 'ref' in childEl ? childEl.ref : undefined;
|
|
51
|
-
if (typeof childRef === 'function') childRef(el);
|
|
52
|
-
else if (childRef && typeof childRef === 'object') (childRef as React.MutableRefObject<unknown>).current = el;
|
|
53
|
-
},
|
|
54
|
-
[children]
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
const [open, setOpen] = useState(false);
|
|
58
|
-
const [isClosing, setIsClosing] = useState(false);
|
|
59
|
-
const [shouldRender, setShouldRender] = useState(false);
|
|
60
|
-
const [position, setPosition] = useState<{ x: number; y: number }>({ x: 0, y: 0 });
|
|
61
|
-
const [tooltipSize, setTooltipSize] = useState<{ width: number; height: number }>({ width: 0, height: 0 });
|
|
62
|
-
const delayTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
63
|
-
const closeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
64
|
-
const viewportPadding = 4;
|
|
65
|
-
|
|
66
|
-
const clearDelayTimer = () => {
|
|
67
|
-
if (delayTimerRef.current) { clearTimeout(delayTimerRef.current); delayTimerRef.current = null; }
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const handleOpen = () => {
|
|
71
|
-
clearDelayTimer();
|
|
72
|
-
const showImmediately = Date.now() - lastTooltipDismissedAt < INSTANT_REOPEN_MS;
|
|
73
|
-
if (showImmediately) {
|
|
74
|
-
setOpen(true); setShouldRender(true); setIsClosing(false); return;
|
|
75
|
-
}
|
|
76
|
-
delayTimerRef.current = setTimeout(() => {
|
|
77
|
-
delayTimerRef.current = null;
|
|
78
|
-
setOpen(true); setShouldRender(true); setIsClosing(false);
|
|
79
|
-
}, resolvedDelay);
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const handleClose = () => {
|
|
83
|
-
clearDelayTimer();
|
|
84
|
-
if (closeTimerRef.current) { clearTimeout(closeTimerRef.current); closeTimerRef.current = null; }
|
|
85
|
-
if (!open && !shouldRender) return;
|
|
86
|
-
lastTooltipDismissedAt = Date.now();
|
|
87
|
-
setIsClosing(true);
|
|
88
|
-
closeTimerRef.current = setTimeout(() => {
|
|
89
|
-
closeTimerRef.current = null;
|
|
90
|
-
setShouldRender(false); setOpen(false); setIsClosing(false);
|
|
91
|
-
}, FADE_OUT_DURATION_MS);
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
useEffect(() => () => { clearDelayTimer(); if (closeTimerRef.current) clearTimeout(closeTimerRef.current); }, []);
|
|
95
|
-
|
|
96
|
-
useLayoutEffect(() => {
|
|
97
|
-
if (!shouldRender || !tooltipRef.current) return;
|
|
98
|
-
const updateSize = () => {
|
|
99
|
-
if (!tooltipRef.current) return;
|
|
100
|
-
const rect = tooltipRef.current.getBoundingClientRect();
|
|
101
|
-
const w = rect.width || tooltipRef.current.offsetWidth;
|
|
102
|
-
const h = rect.height || tooltipRef.current.offsetHeight;
|
|
103
|
-
if (w > 0 && h > 0) setTooltipSize({ width: w, height: h });
|
|
104
|
-
};
|
|
105
|
-
updateSize();
|
|
106
|
-
requestAnimationFrame(updateSize);
|
|
107
|
-
if (typeof ResizeObserver !== 'undefined') {
|
|
108
|
-
const observer = new ResizeObserver(updateSize);
|
|
109
|
-
observer.observe(tooltipRef.current);
|
|
110
|
-
return () => observer.disconnect();
|
|
111
|
-
}
|
|
112
|
-
}, [shouldRender, label, shortcutKey, shortcutKeyPosition]);
|
|
113
|
-
|
|
114
|
-
useLayoutEffect(() => {
|
|
115
|
-
if (!shouldRender || !anchorRef.current || !tooltipRef.current) return;
|
|
116
|
-
const calculatePosition = () => {
|
|
117
|
-
if (!anchorRef.current || !tooltipRef.current) return;
|
|
118
|
-
const a = anchorRef.current.getBoundingClientRect();
|
|
119
|
-
const tw = tooltipRef.current.getBoundingClientRect().width || tooltipSize.width || 80;
|
|
120
|
-
const th = tooltipRef.current.getBoundingClientRect().height || tooltipSize.height || 24;
|
|
121
|
-
let x = 0, y = 0;
|
|
122
|
-
switch (side) {
|
|
123
|
-
case 'top':
|
|
124
|
-
y = a.top - th - sideOffset;
|
|
125
|
-
x = align === 'start' ? a.left + alignOffset : align === 'end' ? a.right - tw + alignOffset : a.left + a.width / 2 - tw / 2 + alignOffset;
|
|
126
|
-
break;
|
|
127
|
-
case 'bottom':
|
|
128
|
-
y = a.bottom + sideOffset;
|
|
129
|
-
x = align === 'start' ? a.left + alignOffset : align === 'end' ? a.right - tw + alignOffset : a.left + a.width / 2 - tw / 2 + alignOffset;
|
|
130
|
-
break;
|
|
131
|
-
case 'left':
|
|
132
|
-
x = a.left - tw - sideOffset;
|
|
133
|
-
y = align === 'start' ? a.top + alignOffset : align === 'end' ? a.bottom - th + alignOffset : a.top + a.height / 2 - th / 2 + alignOffset;
|
|
134
|
-
break;
|
|
135
|
-
case 'right':
|
|
136
|
-
x = a.right + sideOffset;
|
|
137
|
-
y = align === 'start' ? a.top + alignOffset : align === 'end' ? a.bottom - th + alignOffset : a.top + a.height / 2 - th / 2 + alignOffset;
|
|
138
|
-
break;
|
|
139
|
-
}
|
|
140
|
-
setPosition({
|
|
141
|
-
x: Math.min(Math.max(x, viewportPadding), window.innerWidth - tw - viewportPadding),
|
|
142
|
-
y: Math.min(Math.max(y, viewportPadding), window.innerHeight - th - viewportPadding),
|
|
143
|
-
});
|
|
144
|
-
};
|
|
145
|
-
calculatePosition();
|
|
146
|
-
requestAnimationFrame(() => requestAnimationFrame(calculatePosition));
|
|
147
|
-
}, [shouldRender, side, align, sideOffset, alignOffset, tooltipSize.width, tooltipSize.height, label]);
|
|
148
|
-
|
|
149
|
-
useEffect(() => {
|
|
150
|
-
if (!shouldRender) return;
|
|
151
|
-
const onScrollResize = () => {
|
|
152
|
-
if (!anchorRef.current || !tooltipRef.current) return;
|
|
153
|
-
const a = anchorRef.current.getBoundingClientRect();
|
|
154
|
-
const tw = tooltipSize.width || 80;
|
|
155
|
-
const th = tooltipSize.height || 24;
|
|
156
|
-
let x = 0, y = 0;
|
|
157
|
-
switch (side) {
|
|
158
|
-
case 'top': y = a.top - th - sideOffset; x = align === 'start' ? a.left + alignOffset : align === 'end' ? a.right - tw + alignOffset : a.left + a.width / 2 - tw / 2 + alignOffset; break;
|
|
159
|
-
case 'bottom': y = a.bottom + sideOffset; x = align === 'start' ? a.left + alignOffset : align === 'end' ? a.right - tw + alignOffset : a.left + a.width / 2 - tw / 2 + alignOffset; break;
|
|
160
|
-
case 'left': x = a.left - tw - sideOffset; y = align === 'start' ? a.top + alignOffset : align === 'end' ? a.bottom - th + alignOffset : a.top + a.height / 2 - th / 2 + alignOffset; break;
|
|
161
|
-
case 'right': x = a.right + sideOffset; y = align === 'start' ? a.top + alignOffset : align === 'end' ? a.bottom - th + alignOffset : a.top + a.height / 2 - th / 2 + alignOffset; break;
|
|
162
|
-
}
|
|
163
|
-
setPosition({
|
|
164
|
-
x: Math.min(Math.max(x, viewportPadding), window.innerWidth - tw - viewportPadding),
|
|
165
|
-
y: Math.min(Math.max(y, viewportPadding), window.innerHeight - th - viewportPadding),
|
|
166
|
-
});
|
|
167
|
-
};
|
|
168
|
-
window.addEventListener('scroll', onScrollResize, true);
|
|
169
|
-
window.addEventListener('resize', onScrollResize);
|
|
170
|
-
return () => { window.removeEventListener('scroll', onScrollResize, true); window.removeEventListener('resize', onScrollResize); };
|
|
171
|
-
}, [shouldRender, side, align, sideOffset, alignOffset, tooltipSize.width, tooltipSize.height]);
|
|
172
|
-
|
|
173
|
-
const keyHint = shortcutKey ? (
|
|
174
|
-
<div className={styles.keyHint} aria-hidden>
|
|
175
|
-
<Text variant="text-caption-emphasis" as="span" color="primary">{shortcutKey}</Text>
|
|
176
|
-
</div>
|
|
177
|
-
) : null;
|
|
178
|
-
|
|
179
|
-
const tooltipContent = shouldRender ? (
|
|
180
|
-
<div
|
|
181
|
-
ref={tooltipRef}
|
|
182
|
-
className={`${styles.tooltip} ${isClosing ? styles.closing : ''}`}
|
|
183
|
-
style={{
|
|
184
|
-
position: 'fixed',
|
|
185
|
-
left: 0,
|
|
186
|
-
top: 0,
|
|
187
|
-
transform: `translate(${Math.round(position.x)}px, ${Math.round(position.y)}px)`,
|
|
188
|
-
willChange: 'transform',
|
|
189
|
-
zIndex: 10000,
|
|
190
|
-
}}
|
|
191
|
-
role="tooltip"
|
|
192
|
-
>
|
|
193
|
-
<div className={styles.tooltipInner}>
|
|
194
|
-
{shortcutKey && shortcutKeyPosition === 'start' && keyHint}
|
|
195
|
-
<Text variant="text-body-small" as="span" color="primary">{label}</Text>
|
|
196
|
-
{shortcutKey && shortcutKeyPosition === 'end' && keyHint}
|
|
197
|
-
</div>
|
|
198
|
-
</div>
|
|
199
|
-
) : null;
|
|
200
|
-
|
|
201
|
-
const trigger = React.Children.only(children);
|
|
202
|
-
const triggerWithRefAndHandlers = React.isValidElement(trigger)
|
|
203
|
-
? React.cloneElement(trigger as React.ReactElement<any>, {
|
|
204
|
-
ref: setAnchorRef,
|
|
205
|
-
onMouseEnter: (e: React.MouseEvent) => {
|
|
206
|
-
handleOpen();
|
|
207
|
-
(trigger as any).props.onMouseEnter?.(e);
|
|
208
|
-
},
|
|
209
|
-
onMouseLeave: (e: React.MouseEvent) => {
|
|
210
|
-
handleClose();
|
|
211
|
-
(trigger as any).props.onMouseLeave?.(e);
|
|
212
|
-
},
|
|
213
|
-
})
|
|
214
|
-
: children;
|
|
215
|
-
|
|
216
|
-
return (
|
|
217
|
-
<>
|
|
218
|
-
{triggerWithRefAndHandlers}
|
|
219
|
-
{usePortal ? createPortal(tooltipContent, document.body) : tooltipContent}
|
|
220
|
-
</>
|
|
221
|
-
);
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
Tooltip.displayName = 'Tooltip';
|
package/src/css-modules.d.ts
DELETED
package/src/docs/ColorUsage.mdx
DELETED
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { Meta } from '@storybook/addon-docs/blocks';
|
|
2
|
-
|
|
3
|
-
<Meta title="Docs/Color Usage" />
|
|
4
|
-
|
|
5
|
-
# Color Usage Guidelines
|
|
6
|
-
|
|
7
|
-
## 1. Objective
|
|
8
|
-
|
|
9
|
-
This document explains how to use color tokens in product code and design implementation.
|
|
10
|
-
|
|
11
|
-
The key distinction in this repository is:
|
|
12
|
-
|
|
13
|
-
1. semantic colors are for UI meaning, hierarchy, and component states,
|
|
14
|
-
2. data colors are for charts, graphs, and other visualization marks,
|
|
15
|
-
3. both layers are aliases on top of the reference palette.
|
|
16
|
-
|
|
17
|
-
If a choice is available between semantic and data tokens, default to semantic for interface chrome and default to data for visualization marks.
|
|
18
|
-
|
|
19
|
-
## 2. Quick Decision Rule
|
|
20
|
-
|
|
21
|
-
Use this rule first:
|
|
22
|
-
|
|
23
|
-
1. use semantic colors when the color communicates UI role,
|
|
24
|
-
2. use data colors when the color differentiates or encodes data,
|
|
25
|
-
3. use reference colors only when working on the token system itself.
|
|
26
|
-
|
|
27
|
-
Examples:
|
|
28
|
-
|
|
29
|
-
1. page background, card text, button hover, error banner: **semantic**
|
|
30
|
-
2. bar series, line series, pie slices, heatmap steps: **data**
|
|
31
|
-
3. palette generation or alias maintenance: **reference**
|
|
32
|
-
|
|
33
|
-
## 3. Semantic Colors
|
|
34
|
-
|
|
35
|
-
### 3.1 What Semantic Means
|
|
36
|
-
|
|
37
|
-
Semantic tokens are named by purpose, not by visual appearance. They should be chosen based on what the UI element is doing, not based on whether a blue, red, or grey value looks convenient.
|
|
38
|
-
|
|
39
|
-
For example:
|
|
40
|
-
|
|
41
|
-
- `background.primary` means the main surface
|
|
42
|
-
- `foreground.secondary` means lower-emphasis content
|
|
43
|
-
- `background.strong.negative` means a strong negative-intent surface
|
|
44
|
-
- `interaction.hover` means an interaction overlay
|
|
45
|
-
- `foreground.on-strong-background.primary` means primary content placed on a strong colored background
|
|
46
|
-
|
|
47
|
-
### 3.2 When To Use Semantic Colors
|
|
48
|
-
|
|
49
|
-
Use semantic tokens for:
|
|
50
|
-
|
|
51
|
-
1. application and component surfaces,
|
|
52
|
-
2. text and icon hierarchy,
|
|
53
|
-
3. borders and strokes when they convey UI structure,
|
|
54
|
-
4. status styling such as brand, positive, warning, and negative,
|
|
55
|
-
5. hover and pressed overlays,
|
|
56
|
-
6. special product-domain markers that are part of the product language.
|
|
57
|
-
|
|
58
|
-
Do not use semantic tokens to color arbitrary chart series just because a semantic hue looks nice.
|
|
59
|
-
|
|
60
|
-
### 3.3 Common Semantic Families
|
|
61
|
-
|
|
62
|
-
#### Background
|
|
63
|
-
|
|
64
|
-
Use background tokens for surfaces and fills.
|
|
65
|
-
|
|
66
|
-
- `background.primary` and `background.secondary` are standard UI surfaces
|
|
67
|
-
- `background.strong.*`, `background.bold.*`, `background.medium.*`, and `background.faint.*` express emphasis and intent
|
|
68
|
-
- `background.translucent`, `background.shade`, and `background.transparent` are utility surface values
|
|
69
|
-
|
|
70
|
-
Typical mapping:
|
|
71
|
-
|
|
72
|
-
| Use case | Token |
|
|
73
|
-
|----------|-------|
|
|
74
|
-
| Nested card or panel surface | <code>background.primary</code> |
|
|
75
|
-
| App shell or secondary panel | <code>background.secondary</code> |
|
|
76
|
-
| Bold status banner | <code>background.bold.negative</code> |
|
|
77
|
-
| Subtle info container | <code>background.faint.neutral</code> |
|
|
78
|
-
|
|
79
|
-
#### Foreground
|
|
80
|
-
|
|
81
|
-
Use foreground tokens for text, icons, and strokes that follow content hierarchy.
|
|
82
|
-
|
|
83
|
-
- `foreground.primary` is the highest-emphasis content
|
|
84
|
-
- `foreground.secondary` through `foreground.quaternary` reduce emphasis
|
|
85
|
-
- `foreground.strong.*`, `foreground.bold.*`, `foreground.medium.*`, and `foreground.faint.*` provide colored content roles
|
|
86
|
-
- `foreground.on-strong-background.*` is for content placed on strong colored surfaces
|
|
87
|
-
|
|
88
|
-
Typical mapping:
|
|
89
|
-
|
|
90
|
-
| Use case | Token |
|
|
91
|
-
|----------|-------|
|
|
92
|
-
| Main body text | <code>foreground.primary</code> |
|
|
93
|
-
| Helper text or secondary icon | <code>foreground.secondary</code> |
|
|
94
|
-
| Inactive or de-emphasized content | <code>foreground.tertiary</code> |
|
|
95
|
-
| Disabled or very low-emphasis ornament | <code>foreground.quaternary</code> |
|
|
96
|
-
| Text on a bold brand fill | <code>foreground.on-bold-background.primary</code> |
|
|
97
|
-
|
|
98
|
-
> **Note:** `foreground.tertiary` is only about 3.35:1 in light mode and 3.23:1 in dark mode, so it does not meet AA for standard text sizes. Use it only for genuinely low-emphasis content.
|
|
99
|
-
|
|
100
|
-
#### Interaction
|
|
101
|
-
|
|
102
|
-
Use interaction tokens for overlays caused by state changes, not for base surfaces or replacement fills.
|
|
103
|
-
|
|
104
|
-
- `interaction.hover` and `interaction.pressed` are for standard surfaces
|
|
105
|
-
- `interaction.on-strong-background.hover` and similar are for colored fills
|
|
106
|
-
- Interaction tokens preserve the underlying semantic meaning of the surface they sit on
|
|
107
|
-
|
|
108
|
-
#### Border & Divider
|
|
109
|
-
|
|
110
|
-
- `border.primary`, `border.secondary`, and `border.tertiary` are for container edges, field outlines, card strokes
|
|
111
|
-
- `divider.divider` is for internal rules that separate content inside a surface
|
|
112
|
-
- `divider.on-strong-background`, `divider.on-bold-background`, etc. are divider variants for colored fills
|
|
113
|
-
|
|
114
|
-
> **Divider tokens** are narrower in purpose than border tokens. Use them only when drawing a horizontal or vertical rule inside a surface.
|
|
115
|
-
|
|
116
|
-
#### Always-Dark
|
|
117
|
-
|
|
118
|
-
Use `always-dark.*` only when the component must stay dark regardless of surrounding theme. This is a special-case family for fixed dark contexts.
|
|
119
|
-
|
|
120
|
-
### 3.4 Semantic Tone Selection
|
|
121
|
-
|
|
122
|
-
When multiple semantic intensities exist, choose the one that matches the amount of emphasis required:
|
|
123
|
-
|
|
124
|
-
| Tone | Purpose |
|
|
125
|
-
|------|---------|
|
|
126
|
-
| <code>strong</code> | Highest-emphasis fills or content |
|
|
127
|
-
| <code>bold</code> | Strong but less dominant than strong |
|
|
128
|
-
| <code>medium</code> | Mid-emphasis |
|
|
129
|
-
| <code>faint</code> | Low-emphasis tinting or supporting treatment |
|
|
130
|
-
|
|
131
|
-
When a surface uses a strong semantic background, pair it with the matching `on-background` content token rather than reusing normal foreground tokens.
|
|
132
|
-
|
|
133
|
-
## 4. Data Colors
|
|
134
|
-
|
|
135
|
-
### 4.1 When To Use Data Colors
|
|
136
|
-
|
|
137
|
-
Use data tokens for:
|
|
138
|
-
|
|
139
|
-
1. chart series colors,
|
|
140
|
-
2. legend keys,
|
|
141
|
-
3. map overlays and plotted markers,
|
|
142
|
-
4. heatmaps and gradients tied to values,
|
|
143
|
-
5. ordered or diverging color ramps.
|
|
144
|
-
|
|
145
|
-
Do **not** use data tokens for page backgrounds, standard text hierarchy, button states, banners, or form validation.
|
|
146
|
-
|
|
147
|
-
### 4.2 Common Data Families
|
|
148
|
-
|
|
149
|
-
| Family | Purpose | Examples |
|
|
150
|
-
|--------|---------|----------|
|
|
151
|
-
| <code>data-misc</code> | Neutral visualization elements | Gridlines, reference lines, axes |
|
|
152
|
-
| <code>data-category</code> | Nominal categories (unordered) | Multi-series bars, pie slices, legend palettes |
|
|
153
|
-
| <code>data-win-loss</code> | Outcome polarity | Pass/fail, win/loss, positive/negative |
|
|
154
|
-
| <code>data-sequence-*</code> | Ordered single-direction values | Intensity ramps, ordered buckets, choropleths |
|
|
155
|
-
| <code>data-diverging-*</code> | Values extending from a midpoint | Above/below target, deviation from baseline |
|
|
156
|
-
|
|
157
|
-
> **Important:** When using `data-category` colors together, use them in numeric order (1, 2, 3...). This is especially important when categories touch each other (donut charts, stacked bars).
|
|
158
|
-
|
|
159
|
-
### 4.3 How Semantic And Data Work Together
|
|
160
|
-
|
|
161
|
-
| Element | Token source |
|
|
162
|
-
|---------|-------------|
|
|
163
|
-
| Chart card background | <code>background.primary</code> (semantic) |
|
|
164
|
-
| Chart title | <code>foreground.primary</code> (semantic) |
|
|
165
|
-
| Axis labels | <code>foreground.secondary</code> (semantic) |
|
|
166
|
-
| Chart bars/lines | <code>data-category.*</code> or <code>data-sequence-*</code> (data) |
|
|
167
|
-
| Alert banner around chart | <code>background.faint.warning</code> (semantic) |
|
|
168
|
-
|
|
169
|
-
## 5. Accessibility & Pairing
|
|
170
|
-
|
|
171
|
-
### Preferred Text Pairings
|
|
172
|
-
|
|
173
|
-
| Background | Safe foreground | Lowest contrast |
|
|
174
|
-
|-----------|----------------|-----------------|
|
|
175
|
-
| <code>background.primary</code> | <code>foreground.primary</code> or <code>.secondary</code> | ~5.74:1 light, ~6.13:1 dark |
|
|
176
|
-
| <code>background.strong.*</code> | <code>foreground.on-strong-background.primary</code> or <code>.secondary</code> | ~6.54:1 light, ~5.28:1 dark |
|
|
177
|
-
| <code>background.bold.*</code> | <code>foreground.on-bold-background.primary</code> or <code>.secondary</code> | ~5.45:1 light, ~4.89:1 dark |
|
|
178
|
-
|
|
179
|
-
### Tone Pairs
|
|
180
|
-
|
|
181
|
-
- `background.bold.*` with `foreground.faint.*` — safe, lowest floor ~4.54:1
|
|
182
|
-
- `background.faint.*` with `foreground.bold.*` — safe, same reciprocal rule
|
|
183
|
-
- `background.strong.*` with `foreground.medium.*` — near-AA, not guaranteed for every intent
|
|
184
|
-
|
|
185
|
-
## 6. Anti-Patterns
|
|
186
|
-
|
|
187
|
-
Avoid these patterns:
|
|
188
|
-
|
|
189
|
-
1. choosing a token only because the hue looks visually convenient,
|
|
190
|
-
2. using semantic status colors as a chart palette,
|
|
191
|
-
3. using data colors for ordinary UI text or surfaces,
|
|
192
|
-
4. mixing reference tokens directly into product code,
|
|
193
|
-
5. pairing strong backgrounds with regular foreground tokens instead of `on-background` tokens,
|
|
194
|
-
6. using `divider.*` as a generic border token,
|
|
195
|
-
7. changing token meaning across screens without a strong product reason.
|