pixelize-design-library 2.4.2-beta.3 → 2.4.2-beta.30
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/Accordion/Accordion.js +5 -3
- package/dist/Components/AppSwitcher/AppMark.d.ts +19 -0
- package/dist/Components/AppSwitcher/AppMark.js +62 -0
- package/dist/Components/AppSwitcher/AppSwitcher.d.ts +4 -0
- package/dist/Components/AppSwitcher/AppSwitcher.js +315 -0
- package/dist/Components/AppSwitcher/AppSwitcherProps.d.ts +64 -0
- package/dist/Components/AppSwitcher/AppSwitcherProps.js +86 -0
- package/dist/Components/AppSwitcher/useAppSwitcherShortcut.d.ts +3 -0
- package/dist/Components/AppSwitcher/useAppSwitcherShortcut.js +31 -0
- package/dist/Components/AppearanceSettings/AppearanceSettings.js +2 -1
- package/dist/Components/Button/Button.d.ts +2 -2
- package/dist/Components/Button/Button.js +4 -3
- package/dist/Components/Button/Button.styles.js +62 -15
- package/dist/Components/Button/ButtonProps.d.ts +6 -1
- package/dist/Components/ButtonGroupIcon/ButtonGroupIcon.js +6 -6
- package/dist/Components/Card/Card.d.ts +1 -1
- package/dist/Components/Card/Card.js +2 -2
- package/dist/Components/Card/CardProps.d.ts +0 -1
- package/dist/Components/Card/PaymentCard/PaymentCard.js +7 -7
- package/dist/Components/Checkbox/Checkbox.js +1 -1
- package/dist/Components/Checkbox/Checkbox.styles.js +12 -7
- package/dist/Components/Common/fieldStyles.js +1 -1
- package/dist/Components/CopyButton/CopyButton.js +6 -2
- package/dist/Components/CustomModulesTable/EditModuleModal.js +1 -1
- package/dist/Components/DatePicker/CalendarPanel.d.ts +5 -0
- package/dist/Components/DatePicker/CalendarPanel.js +24 -5
- package/dist/Components/DatePicker/DatePickerProps.d.ts +21 -0
- package/dist/Components/DatePicker/RangeDatePicker.js +4 -4
- package/dist/Components/DatePicker/SingleDatePicker.js +2 -2
- package/dist/Components/DatePicker/TimeOnlyPicker.js +1 -1
- package/dist/Components/DatePicker/dayStates.d.ts +56 -0
- package/dist/Components/DatePicker/dayStates.js +87 -0
- package/dist/Components/Dropdown/DropDown.js +7 -15
- package/dist/Components/EmailCards/VerifyEmailOtp/VerifyEmailOtp.js +1 -1
- package/dist/Components/EmptyState/EmptyState.js +2 -1
- package/dist/Components/FilePreview/FilePreview.js +1 -1
- package/dist/Components/FileUpload/FileUpload.js +5 -4
- package/dist/Components/FileUpload/FileUploadProps.d.ts +3 -1
- package/dist/Components/FileUpload/constants.js +1 -0
- package/dist/Components/FilterSidebar/FilterSidebar.js +4 -4
- package/dist/Components/FilterSidebar/filterHoverStyle.d.ts +0 -9
- package/dist/Components/FilterSidebar/filterHoverStyle.js +4 -10
- package/dist/Components/Form/FormWrapper.js +1 -1
- package/dist/Components/Header/components/mobileMenu.js +12 -3
- package/dist/Components/Input/TextInput.styles.js +6 -4
- package/dist/Components/InputTextArea/InputTextArea.style.js +5 -4
- package/dist/Components/KanbanBoard/KanbanBoard.js +5 -45
- package/dist/Components/LimitReachedModal/LimitReachedModal.js +4 -5
- package/dist/Components/Loading/Loading.js +15 -14
- package/dist/Components/MultiSelect/MultiSelect.js +1 -1
- package/dist/Components/NavigationBar/NavigationBar.js +3 -3
- package/dist/Components/NoteTextArea/NoteTextArea.js +9 -4
- package/dist/Components/NumberInput/NumberInput.js +3 -1
- package/dist/Components/NumberInput/NumberInput.styles.js +5 -4
- package/dist/Components/OrgSwitcher/OrgSwitcher.js +2 -2
- package/dist/Components/PdfViewer/PdfViewer.js +6 -1
- package/dist/Components/PinInput/PinInput.js +1 -1
- package/dist/Components/PlanPill/PlanPill.js +5 -5
- package/dist/Components/PlanPill/planPillGradient.d.ts +2 -14
- package/dist/Components/PlanPill/planPillGradient.js +3 -15
- package/dist/Components/Primitives/Primitives.d.ts +6 -0
- package/dist/Components/Primitives/Primitives.js +113 -0
- package/dist/Components/ProductCard/ProductPrice.js +1 -1
- package/dist/Components/ProductCard/ProductReview.js +6 -1
- package/dist/Components/ProductCard/ProductTags.js +1 -1
- package/dist/Components/ProfileCard/ProfileCard.js +1 -1
- package/dist/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +1 -1
- package/dist/Components/RadioButton/RadioButton.js +3 -2
- package/dist/Components/Reorder/Reorder.js +1 -1
- package/dist/Components/ScrollToTop/ScrollToTop.js +2 -0
- package/dist/Components/ScrollbarBox/ScrollbarBox.d.ts +13 -0
- package/dist/Components/ScrollbarBox/ScrollbarBox.js +11 -0
- package/dist/Components/Search/Search.js +2 -2
- package/dist/Components/SearchSelect/SearchSelect.js +14 -9
- package/dist/Components/SegmentedControl/SegmentedControl.d.ts +2 -0
- package/dist/Components/SegmentedControl/SegmentedControl.js +58 -0
- package/dist/Components/SegmentedControl/SegmentedControlProps.d.ts +16 -0
- package/dist/Components/SegmentedControl/SegmentedControlProps.js +2 -0
- package/dist/Components/Select/Select.d.ts +1 -1
- package/dist/Components/Select/Select.js +4 -2
- package/dist/Components/Select/Select.styles.js +3 -2
- package/dist/Components/Select/SelectProps.d.ts +3 -0
- package/dist/Components/SelectSearch/SelectSearch.js +7 -4
- package/dist/Components/SelectV2/SelectV2.js +18 -14
- package/dist/Components/SideBar/SecondaryBar.js +1 -1
- package/dist/Components/SideBar/SideBar.js +1 -1
- package/dist/Components/SideBar/components/MenuPopoverContent.js +2 -2
- package/dist/Components/Slider/Slider.js +8 -1
- package/dist/Components/StageProgress/StageProgress.js +2 -9
- package/dist/Components/StageProgress/StepperStage.js +2 -2
- package/dist/Components/Table/Table.d.ts +1 -1
- package/dist/Components/Table/Table.js +17 -24
- package/dist/Components/Table/TableProps.d.ts +4 -0
- package/dist/Components/Table/components/Pagination.js +1 -1
- package/dist/Components/Table/components/TableActions.js +2 -2
- package/dist/Components/Table/components/TableBody.js +41 -12
- package/dist/Components/Table/components/TableGroupRow.js +1 -1
- package/dist/Components/Table/components/TableHeader.js +21 -8
- package/dist/Components/Table/components/TableSearch.js +1 -1
- package/dist/Components/Table/filters/ActiveFilters.js +2 -18
- package/dist/Components/Table/settings/ManageColumns.js +4 -17
- package/dist/Components/Table/settings/TableSettings.js +5 -11
- package/dist/Components/Table/variants/TableVariantContext.d.ts +4 -0
- package/dist/Components/Table/variants/TableVariantContext.js +17 -0
- package/dist/Components/Table/variants/classic.d.ts +6 -0
- package/dist/Components/Table/variants/classic.js +47 -0
- package/dist/Components/Table/variants/index.d.ts +7 -0
- package/dist/Components/Table/variants/index.js +27 -0
- package/dist/Components/Table/variants/pillInk.d.ts +6 -0
- package/dist/Components/Table/variants/pillInk.js +21 -0
- package/dist/Components/Table/variants/studio.d.ts +6 -0
- package/dist/Components/Table/variants/studio.js +50 -0
- package/dist/Components/Table/variants/tokens.d.ts +49 -0
- package/dist/Components/Table/variants/tokens.js +2 -0
- package/dist/Components/Tag/Tag.styles.js +3 -4
- package/dist/Components/Timeline/Timeline.js +2 -2
- package/dist/Components/Toaster/Toaster.js +1 -1
- package/dist/Components/Toggle/TableToggle.js +11 -7
- package/dist/Components/ToggleSwitch/ToggleSwitch.d.ts +2 -0
- package/dist/Components/ToggleSwitch/ToggleSwitch.js +18 -0
- package/dist/Components/ToggleSwitch/ToggleSwitchProps.d.ts +11 -0
- package/dist/Components/ToggleSwitch/ToggleSwitchProps.js +2 -0
- package/dist/Components/ToolTip/ToolTip.js +6 -6
- package/dist/Components/ToolTip/ToolTipProps.d.ts +3 -1
- package/dist/Components/Trail/TrailWorks.js +1 -1
- package/dist/Components/UpgradeButton/UpgradeButton.js +3 -3
- package/dist/Constants/Sidebar.js +3 -2
- package/dist/Theme/assertPalette.d.ts +1 -1
- package/dist/Theme/assertPalette.js +3 -2
- package/dist/Theme/chakra/Menu.styles.d.ts +3 -0
- package/dist/Theme/chakra/Menu.styles.js +5 -0
- package/dist/Theme/chakra/Popover.styles.d.ts +3 -0
- package/dist/Theme/chakra/Popover.styles.js +5 -1
- package/dist/Theme/chakra/componentStyles.d.ts +6 -0
- package/dist/Theme/chakra/createBrandTheme.js +10 -16
- package/dist/Theme/chakra/focusRing.styles.d.ts +52 -0
- package/dist/Theme/chakra/focusRing.styles.js +46 -0
- package/dist/Theme/provider/ThemeSwitcher.js +5 -2
- package/dist/Theme/tokens/brands/index.js +6 -1
- package/dist/Theme/tokens/brands/zinc/palette.dark.d.ts +1 -0
- package/dist/Theme/tokens/builders/buildDarkPalette.js +6 -1
- package/dist/Theme/tokens/builders/chartColorsFromTheme.js +1 -1
- package/dist/Theme/tokens/builders/focusRing.d.ts +52 -0
- package/dist/Theme/tokens/builders/focusRing.js +92 -0
- package/dist/Theme/tokens/builders/gradientInk.d.ts +22 -0
- package/dist/Theme/tokens/builders/gradientInk.js +28 -0
- package/dist/Theme/tokens/builders/index.d.ts +4 -0
- package/dist/Theme/tokens/builders/index.js +4 -0
- package/dist/Theme/tokens/builders/outlineRung.d.ts +46 -3
- package/dist/Theme/tokens/builders/outlineRung.js +101 -5
- package/dist/Theme/tokens/builders/placeholderInk.d.ts +19 -0
- package/dist/Theme/tokens/builders/placeholderInk.js +35 -0
- package/dist/Theme/tokens/builders/resolvedRungs.d.ts +5 -1
- package/dist/Theme/tokens/builders/resolvedRungs.js +7 -2
- package/dist/Theme/tokens/builders/rowStates.d.ts +13 -0
- package/dist/Theme/tokens/builders/rowStates.js +19 -0
- package/dist/Theme/tokens/builders/scrollbar.d.ts +12 -0
- package/dist/Theme/tokens/builders/scrollbar.js +57 -0
- package/dist/Theme/tokens/builders/surfaceRungs.d.ts +5 -0
- package/dist/Theme/tokens/builders/surfaceRungs.js +20 -5
- package/dist/Theme/tokens/types.d.ts +21 -1
- package/dist/esm/Components/Accordion/Accordion.js +5 -3
- package/dist/esm/Components/AppSwitcher/AppMark.js +56 -0
- package/dist/esm/Components/AppSwitcher/AppSwitcher.js +280 -0
- package/dist/esm/Components/AppSwitcher/AppSwitcherProps.js +78 -0
- package/dist/esm/Components/AppSwitcher/useAppSwitcherShortcut.js +27 -0
- package/dist/esm/Components/AppearanceSettings/AppearanceSettings.js +2 -1
- package/dist/esm/Components/Button/Button.js +5 -4
- package/dist/esm/Components/Button/Button.styles.js +63 -16
- package/dist/esm/Components/ButtonGroupIcon/ButtonGroupIcon.js +6 -6
- package/dist/esm/Components/Card/Card.js +2 -2
- package/dist/esm/Components/Card/PaymentCard/PaymentCard.js +7 -7
- package/dist/esm/Components/Checkbox/Checkbox.js +1 -1
- package/dist/esm/Components/Checkbox/Checkbox.styles.js +13 -8
- package/dist/esm/Components/Common/fieldStyles.js +1 -1
- package/dist/esm/Components/CopyButton/CopyButton.js +7 -3
- package/dist/esm/Components/CustomModulesTable/EditModuleModal.js +1 -1
- package/dist/esm/Components/DatePicker/CalendarPanel.js +24 -5
- package/dist/esm/Components/DatePicker/RangeDatePicker.js +4 -4
- package/dist/esm/Components/DatePicker/SingleDatePicker.js +2 -2
- package/dist/esm/Components/DatePicker/TimeOnlyPicker.js +1 -1
- package/dist/esm/Components/DatePicker/dayStates.js +79 -0
- package/dist/esm/Components/Dropdown/DropDown.js +7 -15
- package/dist/esm/Components/EmailCards/VerifyEmailOtp/VerifyEmailOtp.js +1 -1
- package/dist/esm/Components/EmptyState/EmptyState.js +2 -1
- package/dist/esm/Components/FilePreview/FilePreview.js +1 -1
- package/dist/esm/Components/FileUpload/FileUpload.js +6 -5
- package/dist/esm/Components/FileUpload/constants.js +1 -0
- package/dist/esm/Components/FilterSidebar/FilterSidebar.js +4 -4
- package/dist/esm/Components/FilterSidebar/filterHoverStyle.js +4 -10
- package/dist/esm/Components/Form/FormWrapper.js +1 -1
- package/dist/esm/Components/Header/components/mobileMenu.js +12 -3
- package/dist/esm/Components/Input/TextInput.styles.js +6 -4
- package/dist/esm/Components/InputTextArea/InputTextArea.style.js +5 -4
- package/dist/esm/Components/KanbanBoard/KanbanBoard.js +5 -45
- package/dist/esm/Components/LimitReachedModal/LimitReachedModal.js +4 -5
- package/dist/esm/Components/Loading/Loading.js +12 -11
- package/dist/esm/Components/MultiSelect/MultiSelect.js +1 -1
- package/dist/esm/Components/NavigationBar/NavigationBar.js +3 -3
- package/dist/esm/Components/NoteTextArea/NoteTextArea.js +9 -4
- package/dist/esm/Components/NumberInput/NumberInput.js +4 -2
- package/dist/esm/Components/NumberInput/NumberInput.styles.js +5 -4
- package/dist/esm/Components/OrgSwitcher/OrgSwitcher.js +2 -2
- package/dist/esm/Components/PdfViewer/PdfViewer.js +6 -1
- package/dist/esm/Components/PinInput/PinInput.js +1 -1
- package/dist/esm/Components/PlanPill/PlanPill.js +6 -6
- package/dist/esm/Components/PlanPill/planPillGradient.js +3 -15
- package/dist/esm/Components/Primitives/Primitives.js +6 -0
- package/dist/esm/Components/ProductCard/ProductPrice.js +1 -1
- package/dist/esm/Components/ProductCard/ProductReview.js +6 -1
- package/dist/esm/Components/ProductCard/ProductTags.js +1 -1
- package/dist/esm/Components/ProfileCard/ProfileCard.js +1 -1
- package/dist/esm/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +1 -1
- package/dist/esm/Components/RadioButton/RadioButton.js +3 -2
- package/dist/esm/Components/Reorder/Reorder.js +1 -1
- package/dist/esm/Components/ScrollToTop/ScrollToTop.js +2 -0
- package/dist/esm/Components/ScrollbarBox/ScrollbarBox.js +9 -0
- package/dist/esm/Components/Search/Search.js +2 -2
- package/dist/esm/Components/SearchSelect/SearchSelect.js +14 -9
- package/dist/esm/Components/SegmentedControl/SegmentedControl.js +55 -0
- package/dist/esm/Components/SegmentedControl/SegmentedControlProps.js +1 -0
- package/dist/esm/Components/Select/Select.js +5 -3
- package/dist/esm/Components/Select/Select.styles.js +3 -2
- package/dist/esm/Components/SelectSearch/SelectSearch.js +7 -4
- package/dist/esm/Components/SelectV2/SelectV2.js +18 -14
- package/dist/esm/Components/SideBar/SecondaryBar.js +1 -1
- package/dist/esm/Components/SideBar/SideBar.js +1 -1
- package/dist/esm/Components/SideBar/components/MenuPopoverContent.js +2 -2
- package/dist/esm/Components/Slider/Slider.js +8 -1
- package/dist/esm/Components/StageProgress/StageProgress.js +2 -9
- package/dist/esm/Components/StageProgress/StepperStage.js +2 -2
- package/dist/esm/Components/Table/Table.js +17 -24
- package/dist/esm/Components/Table/components/Pagination.js +1 -1
- package/dist/esm/Components/Table/components/TableActions.js +2 -2
- package/dist/esm/Components/Table/components/TableBody.js +42 -13
- package/dist/esm/Components/Table/components/TableGroupRow.js +1 -1
- package/dist/esm/Components/Table/components/TableHeader.js +21 -8
- package/dist/esm/Components/Table/components/TableSearch.js +1 -1
- package/dist/esm/Components/Table/filters/ActiveFilters.js +2 -18
- package/dist/esm/Components/Table/settings/ManageColumns.js +4 -17
- package/dist/esm/Components/Table/settings/TableSettings.js +5 -11
- package/dist/esm/Components/Table/variants/TableVariantContext.js +13 -0
- package/dist/esm/Components/Table/variants/classic.js +43 -0
- package/dist/esm/Components/Table/variants/index.js +9 -0
- package/dist/esm/Components/Table/variants/pillInk.js +18 -0
- package/dist/esm/Components/Table/variants/studio.js +46 -0
- package/dist/esm/Components/Table/variants/tokens.js +1 -0
- package/dist/esm/Components/Tag/Tag.styles.js +3 -4
- package/dist/esm/Components/Timeline/Timeline.js +2 -2
- package/dist/esm/Components/Toaster/Toaster.js +1 -1
- package/dist/esm/Components/Toggle/TableToggle.js +11 -7
- package/dist/esm/Components/ToggleSwitch/ToggleSwitch.js +15 -0
- package/dist/esm/Components/ToggleSwitch/ToggleSwitchProps.js +1 -0
- package/dist/esm/Components/ToolTip/ToolTip.js +6 -6
- package/dist/esm/Components/Trail/TrailWorks.js +1 -1
- package/dist/esm/Components/UpgradeButton/UpgradeButton.js +3 -3
- package/dist/esm/Constants/Sidebar.js +3 -2
- package/dist/esm/Theme/assertPalette.js +3 -2
- package/dist/esm/Theme/chakra/Menu.styles.js +5 -0
- package/dist/esm/Theme/chakra/Popover.styles.js +5 -1
- package/dist/esm/Theme/chakra/createBrandTheme.js +10 -16
- package/dist/esm/Theme/chakra/focusRing.styles.js +40 -0
- package/dist/esm/Theme/provider/ThemeSwitcher.js +5 -2
- package/dist/esm/Theme/tokens/brands/index.js +6 -1
- package/dist/esm/Theme/tokens/builders/buildDarkPalette.js +6 -1
- package/dist/esm/Theme/tokens/builders/chartColorsFromTheme.js +1 -1
- package/dist/esm/Theme/tokens/builders/focusRing.js +85 -0
- package/dist/esm/Theme/tokens/builders/gradientInk.js +22 -0
- package/dist/esm/Theme/tokens/builders/index.js +4 -0
- package/dist/esm/Theme/tokens/builders/outlineRung.js +96 -4
- package/dist/esm/Theme/tokens/builders/placeholderInk.js +29 -0
- package/dist/esm/Theme/tokens/builders/resolvedRungs.js +7 -2
- package/dist/esm/Theme/tokens/builders/rowStates.js +15 -0
- package/dist/esm/Theme/tokens/builders/scrollbar.js +52 -0
- package/dist/esm/Theme/tokens/builders/surfaceRungs.js +20 -5
- package/dist/esm/index.js +24 -1
- package/dist/index.d.ts +27 -3
- package/dist/index.js +64 -4
- package/package.json +8 -2
|
@@ -2,14 +2,18 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Box, IconButton } from "@chakra-ui/react";
|
|
3
3
|
import { Columns3, Table2 } from "lucide-react";
|
|
4
4
|
import { useCustomTheme } from "../../Theme/useCustomTheme.js";
|
|
5
|
+
import { AA_NON_TEXT, liftToContrast } from "../../Theme/tokens/builders/outlineRung.js";
|
|
5
6
|
import ToolTip from "../ToolTip/ToolTip.js";
|
|
6
7
|
const TableToggle = ({ value, onChange }) => {
|
|
7
8
|
const theme = useCustomTheme();
|
|
9
|
+
const { colors } = theme;
|
|
10
|
+
const restInk = liftToContrast(colors.background[800], colors.background[600], AA_NON_TEXT);
|
|
11
|
+
const hoverInk = liftToContrast(colors.background[900], colors.background[400], AA_NON_TEXT);
|
|
8
12
|
const styles = {
|
|
9
13
|
container: {
|
|
10
14
|
display: "flex",
|
|
11
15
|
alignItems: "center",
|
|
12
|
-
bg:
|
|
16
|
+
bg: colors.background[600],
|
|
13
17
|
p: "0.125rem",
|
|
14
18
|
borderRadius: "md",
|
|
15
19
|
height: "2rem",
|
|
@@ -17,19 +21,19 @@ const TableToggle = ({ value, onChange }) => {
|
|
|
17
21
|
width: "6.3rem",
|
|
18
22
|
},
|
|
19
23
|
button: (mode) => ({
|
|
20
|
-
bg: value === mode ?
|
|
24
|
+
bg: value === mode ? colors.background[50] : "transparent",
|
|
21
25
|
boxShadow: value === mode ? "md" : "none",
|
|
22
|
-
color: value === mode ?
|
|
26
|
+
color: value === mode ? colors.text[900] : restInk,
|
|
23
27
|
borderRadius: "base",
|
|
24
28
|
height: "1.75rem",
|
|
25
29
|
border: "0.031rem solid",
|
|
26
|
-
borderColor: value === mode ?
|
|
30
|
+
borderColor: value === mode ? colors.background[400] : "transparent",
|
|
27
31
|
padding: "0.313rem 0.813rem",
|
|
28
32
|
_hover: {
|
|
29
33
|
bg: value === mode
|
|
30
|
-
?
|
|
31
|
-
:
|
|
32
|
-
color: value === mode ?
|
|
34
|
+
? colors.background[50]
|
|
35
|
+
: colors.background[400],
|
|
36
|
+
color: value === mode ? colors.text[900] : hoverInk,
|
|
33
37
|
},
|
|
34
38
|
transition: "all 0.3s ease-in-out",
|
|
35
39
|
}),
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box } from "@chakra-ui/react";
|
|
3
|
+
import { useCustomTheme } from "../../Theme/useCustomTheme.js";
|
|
4
|
+
const SIZES = {
|
|
5
|
+
sm: { w: "2rem", h: "1.125rem", knob: "0.875rem", travel: "0.75rem" },
|
|
6
|
+
md: { w: "2.375rem", h: "1.375rem", knob: "1.125rem", travel: "0.875rem" },
|
|
7
|
+
};
|
|
8
|
+
export default function ToggleSwitch({ isChecked, onChange, isDisabled = false, size = "md", id, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, }) {
|
|
9
|
+
const { colors } = useCustomTheme();
|
|
10
|
+
const dims = SIZES[size];
|
|
11
|
+
return (_jsx(Box, { as: "button", type: "button", id: id, role: "switch", "aria-checked": isChecked, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, disabled: isDisabled, onClick: () => onChange(!isChecked), position: "relative", display: "inline-flex", alignItems: "center", flexShrink: 0, w: dims.w, h: dims.h, p: 0, borderRadius: "full", borderWidth: "1px", borderStyle: "solid", borderColor: isChecked ? colors.primary[500] : colors.boxborder[500], bg: isChecked ? colors.primary[500] : colors.background[600], opacity: isDisabled ? 0.5 : 1, cursor: isDisabled ? "not-allowed" : "pointer", transition: "background-color 0.2s ease, border-color 0.2s ease", _focusVisible: {
|
|
12
|
+
outline: "none",
|
|
13
|
+
boxShadow: `0 0 0 2px ${colors.primary[300]}`,
|
|
14
|
+
}, children: _jsx(Box, { "aria-hidden": "true", position: "absolute", left: "0.125rem", w: dims.knob, h: dims.knob, borderRadius: "full", bg: colors.background[50], boxShadow: "sm", transition: "transform 0.2s ease", transform: isChecked ? `translateX(${dims.travel})` : "translateX(0)" }) }));
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -4,18 +4,18 @@ import { Tooltip as ChakraTooltip } from "@chakra-ui/react";
|
|
|
4
4
|
import { useIsTruncated } from "../../Hooks/useIsTruncated.js";
|
|
5
5
|
import { mergeRefs } from "../../Utils/mergeRefs.js";
|
|
6
6
|
import { useCustomTheme } from "../../Theme/useCustomTheme.js";
|
|
7
|
-
|
|
7
|
+
import { onFilled } from "../../Theme/tokens/builders/accentText.js";
|
|
8
|
+
export default function ToolTip({ placement = "top", label, children, hasArrow = true, fontSize = "14px", bg, color, isDisabled, overflowOnly = false, isOpen, defaultIsOpen, openDelay = 0, closeDelay = 0, arrowSize = 10, closeOnClick = false, size, width, height, arrowPadding, arrowShadowColor, direction, gutter = 8, onClose, modifiers, closeOnPointerDown, styles, }) {
|
|
8
9
|
// Measure the child for `overflowOnly`. The hook is always called (Rules of
|
|
9
10
|
// Hooks) but is fully inert unless `overflowOnly` is set — no layout reads,
|
|
10
11
|
// no ResizeObserver, no re-renders for ordinary tooltips. Measurement never
|
|
11
12
|
// runs on hover/scroll, so showing the tooltip stays as cheap as before.
|
|
12
13
|
const { ref, isTruncated } = useIsTruncated(label, overflowOnly && isValidElement(children));
|
|
13
|
-
//
|
|
14
|
-
// label. `sidebar.background` is the only ramp `buildDarkPalette` deliberately leaves
|
|
15
|
-
// un-inverted, so it stays near-black in dark mode and the white label stays legible —
|
|
16
|
-
// every other surface ramp flips light and would render the label invisible.
|
|
14
|
+
// `sidebar.background` stays near-black in both modes; ink is computed via `onFilled`.
|
|
17
15
|
const theme = useCustomTheme();
|
|
18
16
|
const surface = theme.colors.sidebar.background[500];
|
|
17
|
+
const resolvedBg = bg !== null && bg !== void 0 ? bg : surface;
|
|
18
|
+
const resolvedColor = color !== null && color !== void 0 ? color : onFilled(resolvedBg, theme.colors.white, theme.colors.black);
|
|
19
19
|
let content = children;
|
|
20
20
|
let effectiveDisabled = isDisabled;
|
|
21
21
|
if (overflowOnly && isValidElement(children)) {
|
|
@@ -26,5 +26,5 @@ export default function ToolTip({ placement = "top", label, children, hasArrow =
|
|
|
26
26
|
});
|
|
27
27
|
effectiveDisabled = Boolean(isDisabled) || !isTruncated;
|
|
28
28
|
}
|
|
29
|
-
return (_jsx(ChakraTooltip, { label: label, placement: placement, hasArrow: hasArrow, fontSize: fontSize, bg:
|
|
29
|
+
return (_jsx(ChakraTooltip, { label: label, placement: placement, hasArrow: hasArrow, fontSize: fontSize, bg: resolvedBg, color: resolvedColor, isDisabled: effectiveDisabled, isOpen: isOpen, defaultIsOpen: defaultIsOpen, openDelay: openDelay, closeDelay: closeDelay, arrowSize: arrowSize, closeOnClick: closeOnClick, size: size, width: width, height: height, arrowPadding: arrowPadding, arrowShadowColor: arrowShadowColor, direction: direction, gutter: gutter, onClose: onClose, modifiers: modifiers, closeOnPointerDown: closeOnPointerDown, style: styles, px: 3, py: 2, borderRadius: "lg", fontWeight: "400", lineHeight: "1.5", maxW: "300px", children: content }));
|
|
30
30
|
}
|
|
@@ -3,7 +3,7 @@ import { Box, Thead, Table, Tr, Th, Tbody, Td, Checkbox, } from "@chakra-ui/reac
|
|
|
3
3
|
import { useCustomTheme } from "../../Theme/useCustomTheme.js";
|
|
4
4
|
const TrailWorks = () => {
|
|
5
5
|
const { colors } = useCustomTheme();
|
|
6
|
-
const cellBorder = colors.
|
|
6
|
+
const cellBorder = colors.boxborder[200];
|
|
7
7
|
const data = [
|
|
8
8
|
{
|
|
9
9
|
contact: "John Doe",
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Box, Flex, Spinner, Text } from "@chakra-ui/react";
|
|
3
3
|
import { Sparkles } from "lucide-react";
|
|
4
4
|
import { useCustomTheme } from "../../Theme/useCustomTheme.js";
|
|
5
|
-
import {
|
|
5
|
+
import { resolveGradient } from "../../Theme/tokens/builders/gradientInk.js";
|
|
6
6
|
import { contrastRatio } from "../../Theme/tokens/builders/color.js";
|
|
7
7
|
import { AA_NON_TEXT } from "../../Theme/tokens/builders/outlineRung.js";
|
|
8
8
|
/** The brand's gold sparkle accent, kept only where it clears 3:1 on the button's own fill. */
|
|
@@ -33,10 +33,10 @@ const UpgradeButton = ({ label, mobileLabel, subtitle, icon = Sparkles, iconColo
|
|
|
33
33
|
danger: { from: c.semantic.error[400], to: c.semantic.error[600] },
|
|
34
34
|
info: { from: c.semantic.info[400], to: c.semantic.info[600] },
|
|
35
35
|
};
|
|
36
|
-
const resolved = gradient !== null && gradient !== void 0 ? gradient : variantGradient[variant];
|
|
37
36
|
// `iconColor`'s gold default fails AA as body text against this gradient (measured ~1.4-3.3
|
|
38
37
|
// across brands) — the label/subtitle share this computed ink instead.
|
|
39
|
-
const
|
|
38
|
+
const resolved = resolveGradient(gradient !== null && gradient !== void 0 ? gradient : variantGradient[variant], c.white, c.black);
|
|
39
|
+
const ink = resolved.ink;
|
|
40
40
|
// The gold only clears 1.4.11's 3:1 on a DARK gradient. zinc dark inverts `primary` to a
|
|
41
41
|
// near-white fill, where gold-on-near-white measures ~1.3:1 and the icon vanishes, so fall
|
|
42
42
|
// back to the label's ink whenever the gold misses either stop.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { SCROLLBAR_WIDTH } from "../Theme/tokens/builders/scrollbar.js";
|
|
1
2
|
export const HEADER_HEIGHT = "4.3rem";
|
|
2
3
|
export const FlexCss = {
|
|
3
4
|
"&::-webkit-scrollbar": {
|
|
4
|
-
width:
|
|
5
|
+
width: SCROLLBAR_WIDTH,
|
|
5
6
|
},
|
|
6
7
|
"&::-webkit-scrollbar-track": {
|
|
7
8
|
background: "transparent",
|
|
@@ -16,7 +17,7 @@ export const FlexCss = {
|
|
|
16
17
|
};
|
|
17
18
|
export const cssForScrollBar = (theme) => ({
|
|
18
19
|
"&::-webkit-scrollbar": {
|
|
19
|
-
width:
|
|
20
|
+
width: SCROLLBAR_WIDTH,
|
|
20
21
|
},
|
|
21
22
|
"&::-webkit-scrollbar-track": {
|
|
22
23
|
background: theme.colors.sidebar.background[500],
|
|
@@ -2,8 +2,8 @@ const REQUIRED_GROUPS = [
|
|
|
2
2
|
"gradient", "primary", "secondary", "tertiary", "transparent", "black", "white",
|
|
3
3
|
"semantic", "gray", "red", "orange", "yellow", "green", "teal", "blue", "cyan",
|
|
4
4
|
"purple", "pink", "backgroundColor", "background", "text", "header", "placeholder",
|
|
5
|
-
"boxShadow", "sidebar", "boxborder", "border", "table", "disabled",
|
|
6
|
-
"textMuted", "semanticText", "onPrimary", "onSemantic",
|
|
5
|
+
"boxShadow", "focusRing", "sidebar", "boxborder", "border", "table", "disabled",
|
|
6
|
+
"accentText", "textMuted", "semanticText", "onPrimary", "onSemantic",
|
|
7
7
|
];
|
|
8
8
|
export const allPaletteTokensListed = true;
|
|
9
9
|
const NESTED = [
|
|
@@ -14,6 +14,7 @@ const NESTED = [
|
|
|
14
14
|
]],
|
|
15
15
|
["semanticText", ["success", "error", "warning", "info"]],
|
|
16
16
|
["onSemantic", ["success", "error", "warning", "info"]],
|
|
17
|
+
["focusRing", ["ring", "halo"]],
|
|
17
18
|
["backgroundColor", ["main", "base"]],
|
|
18
19
|
["boxShadow", ["primary", "default"]],
|
|
19
20
|
];
|
|
@@ -28,6 +28,11 @@ const baseStyle = definePartsStyle(({ theme }) => {
|
|
|
28
28
|
[$bg.variable]: colors.backgroundColor.light,
|
|
29
29
|
bg: $bg.reference,
|
|
30
30
|
color: colors.text[900],
|
|
31
|
+
// A rule at CHROME_RULE weight, not Chakra's gray.200: that default read as a hard edge,
|
|
32
|
+
// which is why this shipped borderless. At 1.31:1 the edge defines the surface instead.
|
|
33
|
+
borderWidth: "1px",
|
|
34
|
+
borderStyle: "solid",
|
|
35
|
+
borderColor: colors.boxborder[200],
|
|
31
36
|
},
|
|
32
37
|
// Own CSS var (`--menu-item-bg`), NOT the list's `--menu-bg` — Chakra's stock item
|
|
33
38
|
// baseStyle rewrites `--menu-bg` itself on hover/focus, so reusing that same var name
|
|
@@ -18,8 +18,12 @@ const baseStyle = definePartsStyle(({ theme }) => {
|
|
|
18
18
|
content: {
|
|
19
19
|
[$popperBg.variable]: colors.backgroundColor.light,
|
|
20
20
|
bg: $popperBg.reference,
|
|
21
|
-
[$arrowShadowColor.variable]: colors.
|
|
21
|
+
[$arrowShadowColor.variable]: colors.boxborder[200],
|
|
22
22
|
color: colors.text[900],
|
|
23
|
+
// Matches Menu: a CHROME_RULE-weight edge, not Chakra's gray.200 hard edge.
|
|
24
|
+
borderWidth: "1px",
|
|
25
|
+
borderStyle: "solid",
|
|
26
|
+
borderColor: colors.boxborder[200],
|
|
23
27
|
},
|
|
24
28
|
};
|
|
25
29
|
});
|
|
@@ -3,6 +3,8 @@ import common from "./common.web.js";
|
|
|
3
3
|
import fonts from "./fonts.web.js";
|
|
4
4
|
import { componentStyles } from "./componentStyles.js";
|
|
5
5
|
import { onFilled } from "../tokens/builders/accentText.js";
|
|
6
|
+
import { globalScrollbarStyles } from "../tokens/builders/scrollbar.js";
|
|
7
|
+
import { focusRingShadow } from "./focusRing.styles.js";
|
|
6
8
|
/**
|
|
7
9
|
* Builds a Chakra theme from a pure brand palette.
|
|
8
10
|
*
|
|
@@ -20,6 +22,12 @@ export const createBrandTheme = (palette, options = {}) => {
|
|
|
20
22
|
colors: palette,
|
|
21
23
|
...fonts,
|
|
22
24
|
...common,
|
|
25
|
+
// `shadows.outline` is what Chakra's stock `_focusVisible` reaches for on EVERY component it
|
|
26
|
+
// themes. The primitive ships one hardcoded blue (`rgba(66,153,225,.6)`), which measured
|
|
27
|
+
// 1.83:1 on the lavender canvas and 1.38:1 on a primary button — below 1.4.11's 3:1 on all
|
|
28
|
+
// nine brands, in both modes. A primitive cannot know the palette; this is the first place
|
|
29
|
+
// that does, so the palette-derived ring is bound here and every stock focus state inherits it.
|
|
30
|
+
shadows: { ...common.shadows, outline: focusRingShadow(palette) },
|
|
23
31
|
};
|
|
24
32
|
return extendTheme({
|
|
25
33
|
...brand,
|
|
@@ -83,22 +91,8 @@ export const createBrandTheme = (palette, options = {}) => {
|
|
|
83
91
|
color: onFilled(palette.primary[500], palette.white, palette.black),
|
|
84
92
|
},
|
|
85
93
|
// `color-scheme: dark` gives WebKit/Blink a dark scrollbar by default, but it's the
|
|
86
|
-
// OS's generic dark gray, not brand-aware
|
|
87
|
-
|
|
88
|
-
"::-webkit-scrollbar": {
|
|
89
|
-
width: "10px",
|
|
90
|
-
height: "10px",
|
|
91
|
-
},
|
|
92
|
-
"::-webkit-scrollbar-track": {
|
|
93
|
-
background: palette.backgroundColor.main,
|
|
94
|
-
},
|
|
95
|
-
"::-webkit-scrollbar-thumb": {
|
|
96
|
-
background: palette.gray[600],
|
|
97
|
-
borderRadius: "6px",
|
|
98
|
-
},
|
|
99
|
-
"::-webkit-scrollbar-thumb:hover": {
|
|
100
|
-
background: palette.gray[500],
|
|
101
|
-
},
|
|
94
|
+
// OS's generic dark gray, not brand-aware.
|
|
95
|
+
...globalScrollbarStyles(palette),
|
|
102
96
|
// Chrome/Edge autofill paints its own opaque yellow/white fill behind the input
|
|
103
97
|
// text via `-webkit-box-shadow` (not `background`), so a plain `bg` override never
|
|
104
98
|
// reaches it — a bright light box survives on every dark autofilled field unless
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WEB binding for the `focusRing` token pair. The colours are derived in
|
|
3
|
+
* `tokens/builders/focusRing.ts` (pure, shared with the mobile library); the CSS shape —
|
|
4
|
+
* box-shadow bands, the forced-colors outline — is web-only and lives here.
|
|
5
|
+
*/
|
|
6
|
+
/** Inner band, in the surface tone: separates the ring from the control's own fill. */
|
|
7
|
+
export const FOCUS_HALO_WIDTH = 2;
|
|
8
|
+
/** Outer edge of the brand band. The band itself is the 2px between halo and ring. */
|
|
9
|
+
export const FOCUS_RING_WIDTH = 4;
|
|
10
|
+
/**
|
|
11
|
+
* The two-tone ring as a single `box-shadow`. Drawn OUTSIDE the border box, so it costs no
|
|
12
|
+
* layout and never resizes the control — the reason this is a shadow and not an outline.
|
|
13
|
+
*/
|
|
14
|
+
export const focusRingShadow = (colors) => `0 0 0 ${FOCUS_HALO_WIDTH}px ${colors.focusRing.halo}, ` +
|
|
15
|
+
`0 0 0 ${FOCUS_RING_WIDTH}px ${colors.focusRing.ring}`;
|
|
16
|
+
/**
|
|
17
|
+
* `:hover`, `:active` and `:focus-visible` all score the same specificity, so which one paints
|
|
18
|
+
* comes down to source order — and in the merged style object Chakra's own Button baseStyle
|
|
19
|
+
* registers `_focusVisible` BEFORE `_hover`. A focused button that is also hovered would then
|
|
20
|
+
* drop the ring for `_hover`'s `boxShadow: "sm"`. Stacking the pseudo-classes raises the
|
|
21
|
+
* specificity above both, so the ring survives whatever else the state repaints.
|
|
22
|
+
*/
|
|
23
|
+
export const FOCUS_VISIBLE_OVER_STATE = "&:hover:focus-visible, &:active:focus-visible, &[data-active]:focus-visible";
|
|
24
|
+
/**
|
|
25
|
+
* `:focus-visible`, never `:focus` — a mouse click must not draw a ring.
|
|
26
|
+
*
|
|
27
|
+
* The transparent outline is not decoration: in Windows High Contrast / `forced-colors` mode
|
|
28
|
+
* box-shadows are dropped entirely, and a transparent outline is the one thing the OS repaints
|
|
29
|
+
* in its own focus colour. Without it, forced-colors users get no indicator at all.
|
|
30
|
+
*/
|
|
31
|
+
export const focusVisibleRing = (colors) => ({
|
|
32
|
+
outline: "2px solid transparent",
|
|
33
|
+
outlineOffset: "2px",
|
|
34
|
+
boxShadow: focusRingShadow(colors),
|
|
35
|
+
});
|
|
36
|
+
/** Both selectors a control needs to keep the ring on top of its own hover/active styling. */
|
|
37
|
+
export const focusVisibleStyles = (colors) => {
|
|
38
|
+
const ring = focusVisibleRing(colors);
|
|
39
|
+
return { _focusVisible: ring, [FOCUS_VISIBLE_OVER_STATE]: ring };
|
|
40
|
+
};
|
|
@@ -23,7 +23,10 @@ const toBrandLabel = (brand) => brand.charAt(0).toUpperCase() + brand.slice(1);
|
|
|
23
23
|
* regardless of which brand/mode is currently active.
|
|
24
24
|
*/
|
|
25
25
|
const brandSwatchColor = (brand) => brandThemes[brand].light.colors.primary[500];
|
|
26
|
-
const BrandSwatch = ({ brand }) =>
|
|
26
|
+
const BrandSwatch = ({ brand }) => {
|
|
27
|
+
const { colors } = useCustomTheme();
|
|
28
|
+
return (_jsx(Box, { as: "span", display: "inline-block", w: "0.625rem", h: "0.625rem", borderRadius: "full", bg: brandSwatchColor(brand), border: "1px solid", borderColor: colors.boxborder[200], flexShrink: 0 }));
|
|
29
|
+
};
|
|
27
30
|
/**
|
|
28
31
|
* Palette values already differ per color mode (`brandThemes[brand].light|dark`), so tokens
|
|
29
32
|
* are read straight from the active theme — no `useColorModeValue` pairing needed here.
|
|
@@ -36,7 +39,7 @@ const useSwitcherColors = () => {
|
|
|
36
39
|
activeColor: colors.primary[500],
|
|
37
40
|
mutedColor: colors.text[600],
|
|
38
41
|
hoverBg: colors.gray[100],
|
|
39
|
-
borderColor: colors.
|
|
42
|
+
borderColor: colors.boxborder[200],
|
|
40
43
|
// NOTE: menuBg lives in Menu.styles.ts — re-pushing it via `sx` flickers on every render.
|
|
41
44
|
menuColor: colors.text[900],
|
|
42
45
|
};
|
|
@@ -22,6 +22,7 @@ import { withSidebarTokens } from "../builders/sidebarAccent.js";
|
|
|
22
22
|
import { buildMutedText } from "../builders/mutedText.js";
|
|
23
23
|
import { buildSemanticText, buildOnSemantic } from "../builders/semanticText.js";
|
|
24
24
|
import { withPlaceholderRung, withTableHoverRung } from "../builders/resolvedRungs.js";
|
|
25
|
+
import { buildFocusRing, withFocusRing } from "../builders/focusRing.js";
|
|
25
26
|
import { withSurfaceRungs } from "../builders/surfaceRungs.js";
|
|
26
27
|
/** Derived, never hand-written: a copied hex goes stale the moment a canvas moves. */
|
|
27
28
|
export const finalizeLightPalette = (source) => ({
|
|
@@ -32,10 +33,14 @@ export const finalizeLightPalette = (source) => ({
|
|
|
32
33
|
semanticText: buildSemanticText(source.semantic, source.backgroundColor.main),
|
|
33
34
|
onPrimary: onFilled(source.primary[500], source.white, source.black),
|
|
34
35
|
onSemantic: buildOnSemantic(source.semantic, source.white, source.black),
|
|
36
|
+
focusRing: buildFocusRing(source),
|
|
35
37
|
});
|
|
36
38
|
export { lavender, meadow, radiant, skyline, slate, emerald, rosewood, storefront, zinc };
|
|
37
39
|
export { lavenderDark, meadowDark, radiantDark, skylineDark, slateDark, emeraldDark, rosewoodDark, storefrontDark, zincDark, };
|
|
38
|
-
|
|
40
|
+
// `withFocusRing` is outermost: it is the only step that reads `primary` and the surfaces
|
|
41
|
+
// TOGETHER, so it must see both after every earlier step — and after a brand file's own
|
|
42
|
+
// post-derivation overrides (zinc dark replaces `primary` and `background` wholesale).
|
|
43
|
+
const resolveRungs = (palette) => withFocusRing(withSurfaceRungs(withTableHoverRung(withPlaceholderRung(withOutlineRung(palette)))));
|
|
39
44
|
const lightBrand = (source) => resolveRungs(finalizeLightPalette(source));
|
|
40
45
|
/** Light palette per brand. `lavender` is the base the other six extend. */
|
|
41
46
|
export const lightPalettes = {
|
|
@@ -4,6 +4,7 @@ import { pickAccentText, onFilled } from "./accentText.js";
|
|
|
4
4
|
import { withSidebarTokens } from "./sidebarAccent.js";
|
|
5
5
|
import { buildMutedText } from "./mutedText.js";
|
|
6
6
|
import { buildSemanticText, buildOnSemantic } from "./semanticText.js";
|
|
7
|
+
import { buildFocusRing } from "./focusRing.js";
|
|
7
8
|
/**
|
|
8
9
|
* Derives a brand's dark palette from its light palette.
|
|
9
10
|
*
|
|
@@ -238,6 +239,7 @@ export function buildDarkPalette(light) {
|
|
|
238
239
|
]));
|
|
239
240
|
const white = light.white;
|
|
240
241
|
const primary = liftBrand(light.primary, white);
|
|
242
|
+
const background = fromTargets(hueSource, SURFACE_TARGETS, SURFACE_SAT);
|
|
241
243
|
const semantic = {
|
|
242
244
|
success: liftAccent(light.semantic.success, white),
|
|
243
245
|
error: liftAccent(light.semantic.error, white),
|
|
@@ -268,7 +270,7 @@ export function buildDarkPalette(light) {
|
|
|
268
270
|
purple: reverseScale(light.purple),
|
|
269
271
|
pink: reverseScale(light.pink),
|
|
270
272
|
backgroundColor: namedSurfaces,
|
|
271
|
-
background
|
|
273
|
+
background,
|
|
272
274
|
text: fromTargets(hueSource, TEXT_TARGETS, TEXT_SAT),
|
|
273
275
|
header: fromTargets(hueSource, TEXT_TARGETS, TEXT_SAT + 2),
|
|
274
276
|
placeholder: fromTargets(hueSource, PLACEHOLDER_TARGETS, PLACEHOLDER_SAT),
|
|
@@ -285,6 +287,9 @@ export function buildDarkPalette(light) {
|
|
|
285
287
|
semanticText: buildSemanticText(semantic, namedSurfaces.main),
|
|
286
288
|
onPrimary: onFilled(primary[500], light.white, light.black),
|
|
287
289
|
onSemantic: buildOnSemantic(semantic, light.white, light.black),
|
|
290
|
+
// The dark ring is derived from the LIFTED accent against the dark canvas, not copied from
|
|
291
|
+
// light: the two modes squeeze it from opposite sides, so a shared value fails one of them.
|
|
292
|
+
focusRing: buildFocusRing({ primary, background, backgroundColor: namedSurfaces }),
|
|
288
293
|
// Sidebar is already near-black in light mode, so it needs no inversion.
|
|
289
294
|
sidebar: withSidebarTokens(light),
|
|
290
295
|
boxShadow: {
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { contrastRatio, hexToHsl, withLightness } from "./color.js";
|
|
2
|
+
import { AA_NON_TEXT } from "./outlineRung.js";
|
|
3
|
+
/** Same lightness granularity `buildDarkPalette` walks its accents at. */
|
|
4
|
+
const LIGHTNESS_STEP = 0.5;
|
|
5
|
+
/**
|
|
6
|
+
* The nearest re-lighting of `hex` that clears `target` against EVERY backdrop at once.
|
|
7
|
+
*
|
|
8
|
+
* Not `settleAcross`: that folds one `liftToContrast` per surface, and each lift walks AWAY from
|
|
9
|
+
* the surface it was handed. That is correct when every backdrop sits on the same side of the ink
|
|
10
|
+
* (its case — a set of light surfaces, or a set of dark ones), and wrong here, where a focus ring
|
|
11
|
+
* is squeezed BETWEEN a near-white canvas and a mid-dark button fill: the lift against the fill
|
|
12
|
+
* walks straight back across the canvas it had just cleared. Seeding `settleAcross` with
|
|
13
|
+
* `primary[500]` measured 1.08-1.99 on the light canvas for every brand for exactly that reason.
|
|
14
|
+
*
|
|
15
|
+
* Walking outward from the seed and testing every backdrop at each step keeps the result the
|
|
16
|
+
* CLOSEST passing colour to the brand accent, so the ring still reads as brand chrome. Contrast is
|
|
17
|
+
* a function of relative luminance alone, and lightness sweeps luminance monotonically from 0 to 1,
|
|
18
|
+
* so if any colour on this hue clears the set, this finds it.
|
|
19
|
+
*/
|
|
20
|
+
export function settleBetween(hex, backdrops, target = AA_NON_TEXT) {
|
|
21
|
+
const clears = (candidate) => backdrops.every((backdrop) => contrastRatio(candidate, backdrop) >= target);
|
|
22
|
+
if (clears(hex))
|
|
23
|
+
return hex;
|
|
24
|
+
const { l } = hexToHsl(hex);
|
|
25
|
+
for (let step = LIGHTNESS_STEP; step <= 100; step += LIGHTNESS_STEP) {
|
|
26
|
+
const darker = withLightness(hex, l - step);
|
|
27
|
+
if (clears(darker))
|
|
28
|
+
return darker;
|
|
29
|
+
const lighter = withLightness(hex, l + step);
|
|
30
|
+
if (clears(lighter))
|
|
31
|
+
return lighter;
|
|
32
|
+
}
|
|
33
|
+
// Unreachable for every shipped brand (the gate proves it) — a brand whose fill and canvas are
|
|
34
|
+
// close enough to leave no gap would land here. Take the end that is least bad rather than throw
|
|
35
|
+
// at import time, and let `focusRing.test.ts` be what reports it.
|
|
36
|
+
const ends = [withLightness(hex, 0), withLightness(hex, 100)];
|
|
37
|
+
const worst = (candidate) => Math.min(...backdrops.map((backdrop) => contrastRatio(candidate, backdrop)));
|
|
38
|
+
return worst(ends[0]) >= worst(ends[1]) ? ends[0] : ends[1];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The three backdrops a focused button's ring is actually drawn against: the page canvas, a card
|
|
42
|
+
* surface, and — because a solid button's ring sits directly against its own fill — `primary[500]`.
|
|
43
|
+
*
|
|
44
|
+
* Deliberately NOT every named surface. `muted`/`accent`/`quaternary` are mid-tone, and a single
|
|
45
|
+
* band asked to clear those AND a dark brand fill has no solution at any hue — contrast is
|
|
46
|
+
* luminance alone, and `skyline`/`storefront` come out with an empty band. That is what the second
|
|
47
|
+
* tone is for: `halo` owns the fill-side adjacency instead.
|
|
48
|
+
*/
|
|
49
|
+
export const focusRingBackdrops = (source) => [
|
|
50
|
+
source.backgroundColor.main,
|
|
51
|
+
source.background[50],
|
|
52
|
+
source.primary[500],
|
|
53
|
+
];
|
|
54
|
+
/**
|
|
55
|
+
* The two-tone `:focus-visible` indicator.
|
|
56
|
+
*
|
|
57
|
+
* Chakra's stock ring (`shadows.outline`, a fixed `rgba(66,153,225,.6)`) measured 1.83:1 on the
|
|
58
|
+
* lavender canvas and 1.38:1 on a primary button — a focus indicator you cannot see, on all nine
|
|
59
|
+
* brands. It is one hardcoded blue, so no palette could have saved it.
|
|
60
|
+
*
|
|
61
|
+
* `halo` is the surface tone, drawn as the inner band so the indicator separates from a filled
|
|
62
|
+
* control whatever `colorScheme` painted it. `ring` is the brand accent, walked to the nearest
|
|
63
|
+
* lightness that clears 3:1 on all three backdrops at once — including the halo, so the two tones
|
|
64
|
+
* are always told apart.
|
|
65
|
+
*/
|
|
66
|
+
export const buildFocusRing = (source) => {
|
|
67
|
+
const halo = settleBetween(source.background[50], [source.primary[500]]);
|
|
68
|
+
return {
|
|
69
|
+
halo,
|
|
70
|
+
ring: settleBetween(source.primary[500], [...focusRingBackdrops(source), halo]),
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Re-derives the ring from the FINISHED palette, as the last step of the rung chain.
|
|
75
|
+
*
|
|
76
|
+
* `finalizeLightPalette` and `buildDarkPalette` seed it, but neither is the final word: a brand
|
|
77
|
+
* file may replace `primary` or `background` after the generic derivation runs — `zinc`'s dark
|
|
78
|
+
* palette replaces both, inverting the filled button to near-white — and a ring resolved against
|
|
79
|
+
* the pre-override values is then silently wrong. Same reason `withSurfaceRungs` re-lifts inks the
|
|
80
|
+
* finalizers already derived. `focusRing.test.ts` asserts this is a fixed point.
|
|
81
|
+
*/
|
|
82
|
+
export const withFocusRing = (palette) => ({
|
|
83
|
+
...palette,
|
|
84
|
+
focusRing: buildFocusRing(palette),
|
|
85
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { onGradient } from "./accentText.js";
|
|
2
|
+
import { resolveAvatarFill } from "./statusTone.js";
|
|
3
|
+
/**
|
|
4
|
+
* Sinks both stops until WHITE ink clears AA, keeping each stop's hue. Any mid-tone gradient
|
|
5
|
+
* otherwise flips `onGradient` to black ink, so the same element reads white-on-colour in one
|
|
6
|
+
* brand and black-on-pastel in the next.
|
|
7
|
+
*/
|
|
8
|
+
export const inkSafeGradient = (gradient) => ({
|
|
9
|
+
from: resolveAvatarFill(gradient.from),
|
|
10
|
+
to: resolveAvatarFill(gradient.to),
|
|
11
|
+
});
|
|
12
|
+
/**
|
|
13
|
+
* The only correct way to paint text on a gradient: returns the stops to draw AND the ink for
|
|
14
|
+
* them. Calling `onGradient` on raw stops is the black-text bug — it picks ink for a backdrop
|
|
15
|
+
* that is then painted differently.
|
|
16
|
+
*/
|
|
17
|
+
export const resolveGradient = (gradient, white, black) => {
|
|
18
|
+
const { from, to } = inkSafeGradient(gradient);
|
|
19
|
+
return { from, to, ink: onGradient(from, to, white, black) };
|
|
20
|
+
};
|
|
21
|
+
/** `linear-gradient(...)` for a resolved pair — one spelling of the CSS everywhere. */
|
|
22
|
+
export const gradientCss = (g, angle = "135deg") => `linear-gradient(${angle}, ${g.from} 0%, ${g.to} 100%)`;
|
|
@@ -3,3 +3,7 @@ export * from "./buildBrandTokens.js";
|
|
|
3
3
|
export * from "./buildDarkPalette.js";
|
|
4
4
|
export * from "./chartColorsFromTheme.js";
|
|
5
5
|
export * from "./color.js";
|
|
6
|
+
export * from "./focusRing.js";
|
|
7
|
+
export * from "./scrollbar.js";
|
|
8
|
+
export * from "./gradientInk.js";
|
|
9
|
+
export * from "./placeholderInk.js";
|
|
@@ -7,6 +7,35 @@ import { contrastRatio, hexToHsl, relativeLuminance, withLightness } from "./col
|
|
|
7
7
|
export const MIN_PERCEPTIBLE_STEP = 1.15;
|
|
8
8
|
/** WCAG 1.4.11 — a control outline is a UI component boundary, held to 3:1. */
|
|
9
9
|
export const AA_NON_TEXT = 3;
|
|
10
|
+
/**
|
|
11
|
+
* Resting chrome — card, popover and field outlines — settles here, not at `AA_NON_TEXT`.
|
|
12
|
+
* At 3:1 a container border is text-weight and the UI reads as a wireframe; every mainstream
|
|
13
|
+
* system draws this line far lighter (Tailwind gray-200 1.24, Chakra gray.200 1.30, Material
|
|
14
|
+
* outline-variant 1.61). 1.4.11 asks that a control be identifiable, not that its resting
|
|
15
|
+
* border carry the whole job: the fill, the label and the focus ring do that.
|
|
16
|
+
*
|
|
17
|
+
* That defence was untrue when it was written. Buttons defined no `_focusVisible` at all, so the
|
|
18
|
+
* focus ring was Chakra's stock `shadows.outline` — a fixed blue measuring 1.38:1 on a primary
|
|
19
|
+
* button — and nothing in the library was held to `AA_NON_TEXT` at the moment of focus. It is
|
|
20
|
+
* true now: `focusRing` (`builders/focusRing.ts`) is derived per brand per mode and gated at 3:1
|
|
21
|
+
* on the canvas, on a card and on the button's own fill by `builders/focusRing.test.ts`. If that
|
|
22
|
+
* gate is ever relaxed, this constant loses its justification with it.
|
|
23
|
+
*/
|
|
24
|
+
export const CHROME_BORDER = 1.5;
|
|
25
|
+
// The resting rule between two surfaces — a card edge, a divider, a menu outline. Lighter than
|
|
26
|
+
// CHROME_BORDER, which outlines a control the user acts on.
|
|
27
|
+
export const CHROME_RULE = 1.31;
|
|
28
|
+
/**
|
|
29
|
+
* A rule is decorative, so it has a ceiling and no floor: soften it until the surface it reads
|
|
30
|
+
* hardest against sits at `target`, and leave a already-fainter rung alone. Never `settleAcross`
|
|
31
|
+
* here — that clears a floor on EVERY surface, so one tinted surface drags the rule darker.
|
|
32
|
+
*/
|
|
33
|
+
export function ruleInk(hex, surfaces, target) {
|
|
34
|
+
const loudest = surfaces.reduce((worst, s) => contrastRatio(hex, s) > contrastRatio(hex, worst) ? s : worst);
|
|
35
|
+
return contrastRatio(hex, loudest) > target
|
|
36
|
+
? softenToContrast(hex, loudest, target)
|
|
37
|
+
: hex;
|
|
38
|
+
}
|
|
10
39
|
/**
|
|
11
40
|
* Walks a hex toward `target` contrast against `backdrop`, keeping hue and saturation.
|
|
12
41
|
* Direction is DERIVED from the backdrop, never passed: a mode argument here was redundant
|
|
@@ -30,11 +59,30 @@ export function liftToContrast(hex, backdrop, target = AA_NON_TEXT) {
|
|
|
30
59
|
const ends = [withLightness(hex, 100, s), withLightness(hex, 0, s)];
|
|
31
60
|
return contrastRatio(ends[0], backdrop) >= contrastRatio(ends[1], backdrop) ? ends[0] : ends[1];
|
|
32
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Mirror of `liftToContrast`: walks ink TOWARD the backdrop, stopping at the last step that
|
|
64
|
+
* still clears `target`. For ink that must stay legible without reading as body copy.
|
|
65
|
+
*/
|
|
66
|
+
export function softenToContrast(hex, backdrop, target) {
|
|
67
|
+
if (contrastRatio(hex, backdrop) <= target)
|
|
68
|
+
return hex;
|
|
69
|
+
const { s, l: start } = hexToHsl(hex);
|
|
70
|
+
const toward = relativeLuminance(backdrop) < 0.5 ? -1 : 1;
|
|
71
|
+
let softest = hex;
|
|
72
|
+
for (let l = start; l > 0 && l < 100; l += toward) {
|
|
73
|
+
const next = withLightness(hex, l, s);
|
|
74
|
+
if (contrastRatio(next, backdrop) < target)
|
|
75
|
+
break;
|
|
76
|
+
softest = next;
|
|
77
|
+
}
|
|
78
|
+
return softest;
|
|
79
|
+
}
|
|
33
80
|
/**
|
|
34
81
|
* `boxborder[500]` is the rung every control outline reads (Checkbox, SearchSelect, the date
|
|
35
|
-
* pickers, the editor's inputs).
|
|
36
|
-
*
|
|
37
|
-
*
|
|
82
|
+
* pickers, the editor's inputs). Settled onto `CHROME_BORDER` — lifted when a brand authored it
|
|
83
|
+
* invisibly pale, softened when it overshoots. It was previously lifted to `AA_NON_TEXT`, which
|
|
84
|
+
* shipped 3.85-4.95:1 outlines: a text-weight box around every card and popover.
|
|
85
|
+
* Resolved here rather than hand-pinned per brand: a copied hex goes stale the moment a canvas moves.
|
|
38
86
|
*
|
|
39
87
|
* `border[*]` is deliberately untouched — that ramp draws table gridlines, which are decorative
|
|
40
88
|
* and outside 1.4.11.
|
|
@@ -47,7 +95,9 @@ export function withOutlineRung(palette) {
|
|
|
47
95
|
contrastRatio(current, palette.background[50])
|
|
48
96
|
? palette.backgroundColor.main
|
|
49
97
|
: palette.background[50];
|
|
50
|
-
const resolved =
|
|
98
|
+
const resolved = contrastRatio(current, backdrop) > CHROME_BORDER
|
|
99
|
+
? softenToContrast(current, backdrop, CHROME_BORDER)
|
|
100
|
+
: liftToContrast(current, backdrop, CHROME_BORDER);
|
|
51
101
|
if (resolved === current)
|
|
52
102
|
return palette;
|
|
53
103
|
return { ...palette, boxborder: { ...palette.boxborder, 500: resolved } };
|
|
@@ -63,3 +113,45 @@ export function withOutlineRung(palette) {
|
|
|
63
113
|
export function resolveHoverTint(seed, restSurface) {
|
|
64
114
|
return liftToContrast(seed, restSurface, MIN_PERCEPTIBLE_STEP);
|
|
65
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Settles ink into `target` against EVERY surface at once. Softening one step can change which
|
|
118
|
+
* surface is hardest, so resolving against a single pre-computed "hardest" one lands under the
|
|
119
|
+
* floor on another — that is how radiant and rosewood ended up with an invisible outline on
|
|
120
|
+
* their muted surface.
|
|
121
|
+
*/
|
|
122
|
+
export function settleAcross(hex, surfaces, target) {
|
|
123
|
+
const clearsAll = (c) => surfaces.every((s) => contrastRatio(c, s) >= target);
|
|
124
|
+
if (!clearsAll(hex)) {
|
|
125
|
+
return surfaces.reduce((ink, s) => liftToContrast(ink, s, target), hex);
|
|
126
|
+
}
|
|
127
|
+
const { s: sat, l: start } = hexToHsl(hex);
|
|
128
|
+
const toward = relativeLuminance(surfaces[0]) < 0.5 ? -1 : 1;
|
|
129
|
+
let settled = hex;
|
|
130
|
+
for (let l = start; l > 0 && l < 100; l += toward) {
|
|
131
|
+
const next = withLightness(hex, l, sat);
|
|
132
|
+
if (!clearsAll(next))
|
|
133
|
+
break;
|
|
134
|
+
settled = next;
|
|
135
|
+
}
|
|
136
|
+
return settled;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* A pure-grey surface sitting `target` contrast away from `backdrop`. Binary search, not a walk:
|
|
140
|
+
* `liftToContrast` cannot move a seed already at lightness 0 or 100, and a pure-white panel seeded
|
|
141
|
+
* itself straight to black. Neutral on purpose — the light palettes are hand-authored and tinted,
|
|
142
|
+
* so lifting one of their surface rungs turns a selected row into a saturated brand block, while
|
|
143
|
+
* the derived dark palettes land on grey and look right.
|
|
144
|
+
*/
|
|
145
|
+
export function neutralStep(backdrop, target) {
|
|
146
|
+
const grey = (l) => withLightness("#808080", l, 0);
|
|
147
|
+
let lo = hexToHsl(backdrop).l;
|
|
148
|
+
let hi = relativeLuminance(backdrop) < 0.5 ? 100 : 0;
|
|
149
|
+
for (let i = 0; i < 20; i += 1) {
|
|
150
|
+
const mid = (lo + hi) / 2;
|
|
151
|
+
if (contrastRatio(grey(mid), backdrop) < target)
|
|
152
|
+
lo = mid;
|
|
153
|
+
else
|
|
154
|
+
hi = mid;
|
|
155
|
+
}
|
|
156
|
+
return grey(hi);
|
|
157
|
+
}
|