diff --git a/src/components/ActivityToast/ActivityToast.tsx b/src/components/ActivityToast/ActivityToast.tsx index fa3f54b..1df8d0d 100644 --- a/src/components/ActivityToast/ActivityToast.tsx +++ b/src/components/ActivityToast/ActivityToast.tsx @@ -5,10 +5,12 @@ import { Anchor, BackgroundColor, bgColorClass, + BorderColor, + borderColorClass, Radius, Variant, } from "@/types"; -import { cn } from "@/util/classes.ts"; +import { cn } from "@/util/classes"; import { textColor } from "@/styles/text"; import radiusStyles from "@/styles/radius"; @@ -38,7 +40,8 @@ export const ActivityToast: FC = ({ "gap-x-sm", "items-center", "py-2 pr-4 pl-3", - "border border-content-border-secondary-primary", + "border", + borderColorClass(BorderColor.Default), bgColorClass(BackgroundColor.Card1), radiusStyles(Radius.Md), className diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index a79f9a3..b973d03 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -1,8 +1,19 @@ -import { Size, Variant } from "@/types"; +import { + ActionColor, + bgColorClass, + BorderColor, + borderColorClass, + ElementState, + Radius, + Size, + textColorClass, + Variant, +} from "@/types"; import { cn } from "@/util/classes"; import { Button as HeadlessButton } from "@headlessui/react"; import clsx from "clsx"; import type { ButtonHTMLAttributes, FC } from "react"; +import radiusStyles from "@/styles/radius"; type ButtonSize = Exclude; @@ -16,30 +27,33 @@ export interface ButtonProps extends ButtonHTMLAttributes { const variantStyles: Record = { [Variant.Primary]: clsx( - "bg-action-primary-primary", - "hover:bg-action-primary-secondary", - "active:bg-action-primary-tertiary", - "text-action-primary-text" + bgColorClass(ActionColor.PrimaryDefault), + bgColorClass(ActionColor.PrimaryHover, ElementState.Hover), + bgColorClass(ActionColor.PrimaryFocus, ElementState.Active), + textColorClass(ActionColor.PrimaryText) ), [Variant.Secondary]: clsx( "border-1", - "bg-action-secondary-primary border-content-border-secondary-primary", - "hover:bg-action-secondary-secondary hover:border-content-border-secondary-secondary", - "active:bg-action-secondary-tertiary active:border-content-border-secondary-tertiary", - "text-action-secondary-text", - "disabled:border-content-border-secondary-disabled" + bgColorClass(ActionColor.SecondaryDefault), + borderColorClass(BorderColor.Default), + bgColorClass(ActionColor.SecondaryHover, ElementState.Hover), + borderColorClass(BorderColor.Hover, ElementState.Hover), + bgColorClass(ActionColor.SecondaryFocus, ElementState.Active), + borderColorClass(BorderColor.Focus, ElementState.Active), + textColorClass(ActionColor.SecondaryText), + borderColorClass(BorderColor.Disabled, ElementState.Disabled) ), [Variant.Success]: clsx( - "bg-action-success-primary", - "hover:bg-action-success-secondary", - "active:bg-action-success-tertiary", - "text-action-success-text" + bgColorClass(ActionColor.SuccessDefault), + bgColorClass(ActionColor.SuccessHover, ElementState.Hover), + bgColorClass(ActionColor.SuccessFocus, ElementState.Active), + textColorClass(ActionColor.SuccessText) ), [Variant.Danger]: clsx( - "bg-action-danger-primary", - "hover:bg-action-danger-secondary", - "active:bg-action-danger-tertiary", - "text-action-danger-text" + bgColorClass(ActionColor.DangerDefault), + bgColorClass(ActionColor.DangerHover, ElementState.Hover), + bgColorClass(ActionColor.DangerFocus, ElementState.Active), + textColorClass(ActionColor.DangerText) ), }; @@ -69,7 +83,7 @@ export const Button: FC = ({ , @@ -57,7 +67,7 @@ export const Checkbox: FC = ({ "cursor-pointer", "appearance-none", "border", - "border-content-border-secondary-primary", + borderColorClass(BorderColor.Default), "group-hover:border-action-primary-primary", radiusStyles(radius), sizeStyles[size], @@ -68,8 +78,8 @@ export const Checkbox: FC = ({ "focus:ring-offset-2", "disabled:opacity-50", "disabled:cursor-not-allowed", - "data-checked:bg-action-primary-primary", - "data-checked:border-action-primary-primary", + bgColorClass(ActionColor.PrimaryDefault, ElementState.Checked), + borderColorClass(BorderColor.Active, ElementState.Checked), className )} {...props} diff --git a/src/components/FormField/FormField.tsx b/src/components/FormField/FormField.tsx index c3e78cc..b46ca1d 100644 --- a/src/components/FormField/FormField.tsx +++ b/src/components/FormField/FormField.tsx @@ -40,7 +40,7 @@ export const FormField: FC = ({ {control} - {error && {error}} + {error && {error}} ); diff --git a/src/components/Heading/Heading.tsx b/src/components/Heading/Heading.tsx index 374f651..0aa5dbd 100644 --- a/src/components/Heading/Heading.tsx +++ b/src/components/Heading/Heading.tsx @@ -1,5 +1,5 @@ import type { FC, HTMLAttributes } from "react"; -import { TextVariant } from "@/types"; +import { TextColor, textColorClass, TextVariant } from "@/types"; import HeadingLevel from "@/types/heading"; import { textStyles } from "@/styles/text"; import clsx from "clsx"; @@ -33,7 +33,7 @@ export const Heading: FC = ({ return ( , "size" | "className" @@ -44,20 +55,24 @@ export const Input: FC = ({ }) => { const inputClasses = cn( "w-full", - "bg-content-background-primary", - "text-content-text-primary", + bgColorClass(BackgroundColor.Background), + textColorClass(TextColor.Primary), "placeholder:text-content-text-tertiary", "transition-colors", "border", - error ? "border-semantic-destructive" : "border-content-border-default", - !disabled && !error && "hover:border-content-border-hover", + error + ? borderColorClass(BorderColor.Error) + : borderColorClass(BorderColor.Default), + !disabled && + !error && + borderColorClass(BorderColor.Hover, ElementState.Hover), "focus:outline-none", error - ? "focus:border-semantic-destructive" - : "focus:border-content-border-focus", + ? borderColorClass(BorderColor.Error, ElementState.Focus) + : borderColorClass(BorderColor.Focus, ElementState.Focus), "disabled:opacity-50", "disabled:cursor-not-allowed", - "disabled:border-content-border-secondary-disabled", + borderColorClass(BorderColor.Disabled, ElementState.Disabled), radiusStyles(radius), sizeStyles[size], Icon ? paddingLeftStyles[size] : "pl-3", diff --git a/src/components/Input/InputIcon.spec.tsx b/src/components/Input/InputIcon.spec.tsx index 8ccc744..ba79bab 100644 --- a/src/components/Input/InputIcon.spec.tsx +++ b/src/components/Input/InputIcon.spec.tsx @@ -1,4 +1,4 @@ -import { Size } from "@/types"; +import { Size, TextColor, textColorClass } from "@/types"; import { render } from "@testing-library/react"; import { CheckmarkIcon } from "../Icons/Checkmark"; import { InputIcon } from "./InputIcon"; @@ -12,21 +12,21 @@ describe("InputIcon", () => { expect(svg).toBeInTheDocument(); }); - it("should apply text-content-text-primary when hasText is true", () => { + it("should apply correct styling when hasText is true", () => { const { container } = render( ); const span = container.querySelector("span"); - expect(span).toHaveClass("text-content-text-primary"); - expect(span).not.toHaveClass("text-content-text-secondary"); + expect(span).toHaveClass(textColorClass(TextColor.Primary)); + expect(span).not.toHaveClass(textColorClass(TextColor.Secondary)); }); - it("should apply text-content-text-secondary when hasText is false", () => { + it("should apply correct styling when hasText is false", () => { const { container } = render( ); const span = container.querySelector("span"); - expect(span).toHaveClass("text-content-text-secondary"); - expect(span).not.toHaveClass("text-content-text-primary"); + expect(span).toHaveClass(textColorClass(TextColor.Secondary)); + expect(span).not.toHaveClass(textColorClass(TextColor.Primary)); }); }); diff --git a/src/components/Input/InputIcon.tsx b/src/components/Input/InputIcon.tsx index 095ae1d..7c6a405 100644 --- a/src/components/Input/InputIcon.tsx +++ b/src/components/Input/InputIcon.tsx @@ -1,7 +1,7 @@ -import { Size } from "@/types"; +import { Size, TextColor, textColorClass } from "@/types"; import { cn } from "@/util/classes"; import { type FC } from "react"; -import { IconProps } from "../Icons/types"; +import { IconProps } from "@/components/Icons/types"; import { iconPaddingStyles, iconSizeStyles } from "./styles"; export interface InputIconProps { @@ -15,7 +15,9 @@ export const InputIcon: FC = ({ Icon, size, hasText }) => { diff --git a/src/components/ListItem/ListItem.tsx b/src/components/ListItem/ListItem.tsx index 58cbfe4..6f3dc3c 100644 --- a/src/components/ListItem/ListItem.tsx +++ b/src/components/ListItem/ListItem.tsx @@ -3,8 +3,16 @@ import type { SyntheticListenerMap } from "@dnd-kit/core/dist/hooks/utilities"; import clsx from "clsx"; import { DragHandleIcon } from "@/components/Icons/DragHandle"; import { Text } from "@/components/Text"; -import { TextColor, TextVariant } from "@/types"; +import { + BackgroundColor, + bgColorClass, + Radius, + TextColor, + textColorClass, + TextVariant, +} from "@/types"; import { Checkbox } from "@/components/Checkbox"; +import radiusStyles from "@/styles/radius"; export interface ListItemProps extends HTMLAttributes { canSelect?: boolean; @@ -33,11 +41,11 @@ export const ListItem: FC = ({
= ({ className="flex align-items cursor-grab touch-none" {...dragHandleListeners} > - + )} {primaryContent} diff --git a/src/components/Pill/Pill.tsx b/src/components/Pill/Pill.tsx index c77b9a4..eb71c6c 100644 --- a/src/components/Pill/Pill.tsx +++ b/src/components/Pill/Pill.tsx @@ -1,11 +1,20 @@ import radiusStyles from "@/styles/radius"; import shadowStyles from "@/styles/shadow"; -import { BackgroundColor, Radius, Shadow, Size, TextColor } from "@/types"; +import { + BackgroundColor, + Radius, + SemanticColor, + Shadow, + Size, + StatusColor, + TextColor, +} from "@/types"; import { bgColorClass, textColorClass } from "@/types/color"; import clsx from "clsx"; import type { FC, HTMLAttributes } from "react"; -type PillSize = Exclude; +export type PillSize = Exclude; +export type PillColor = BackgroundColor | SemanticColor | StatusColor; export interface PillProps extends HTMLAttributes { size?: PillSize; @@ -13,7 +22,7 @@ export interface PillProps extends HTMLAttributes { shadow?: Shadow; color?: TextColor; isStatus?: boolean; - backgroundColor?: BackgroundColor; + backgroundColor?: PillColor; } const sizeStyles: Record = { diff --git a/src/components/RichButton/RichButton.tsx b/src/components/RichButton/RichButton.tsx index 53302a4..b77a77a 100644 --- a/src/components/RichButton/RichButton.tsx +++ b/src/components/RichButton/RichButton.tsx @@ -1,10 +1,18 @@ import type { FC, HTMLAttributes, ReactNode } from "react"; -import { cn } from "@/util/classes.ts"; -import radiusStyles from "@/styles/radius.ts"; -import { Radius, TextColor, textColorClass } from "@/types"; +import { cn } from "@/util/classes"; +import { + BorderColor, + borderColorClass, + ElementState, + IconColor, + Radius, + TextColor, + textColorClass, +} from "@/types"; import { Text } from "@/components/Text"; import { Clickable } from "@/components/Clickable"; import clsx from "clsx"; +import radiusStyles from "@/styles/radius"; export interface RichButtonProps extends HTMLAttributes { active?: boolean; @@ -28,9 +36,9 @@ export const RichButton: FC = ({ className={clsx( "border", active - ? "border-action-primary-primary" - : "border-content-border-secondary-primary", - !active && "hover:border-content-border-secondary-secondary", + ? borderColorClass(BorderColor.Active) + : borderColorClass(BorderColor.Default), + !active && borderColorClass(BorderColor.Hover, ElementState.Hover), "p-3", radiusStyles(Radius.Md), className @@ -45,8 +53,8 @@ export const RichButton: FC = ({ className={cn( "size-5", active - ? textColorClass(TextColor.BrandPrimary) - : textColorClass(TextColor.Primary) + ? textColorClass(IconColor.Brand) + : textColorClass(IconColor.Default) )} > diff --git a/src/components/Text/Text.tsx b/src/components/Text/Text.tsx index 98870ba..0ada82e 100644 --- a/src/components/Text/Text.tsx +++ b/src/components/Text/Text.tsx @@ -1,12 +1,12 @@ import { textStyles } from "@/styles/text"; -import { TextColor, TextVariant } from "@/types"; +import { IconColor, TextColor, TextVariant } from "@/types"; import { textColorClass } from "@/types/color"; -import clsx from "clsx"; import type { FC, HTMLAttributes } from "react"; +import clsx from "clsx"; export interface TextProps extends HTMLAttributes { variant?: TextVariant; - color?: TextColor; + color?: TextColor | IconColor; } export const Text: FC = ({ diff --git a/src/components/TextBadge/TextBadge.tsx b/src/components/TextBadge/TextBadge.tsx index 8d2503c..8f96205 100644 --- a/src/components/TextBadge/TextBadge.tsx +++ b/src/components/TextBadge/TextBadge.tsx @@ -1,13 +1,13 @@ import type { FC, HTMLAttributes } from "react"; -import { TextColor, TextVariant } from "@/types"; +import { IconColor, TextColor, TextVariant } from "@/types"; import { Text } from "@/components/Text"; export interface TextBadgeProps extends HTMLAttributes { - color?: TextColor; + color?: TextColor | IconColor; } export const TextBadge: FC = ({ - color = TextColor.BrandPrimary, + color = IconColor.Brand, children, ...props }) => ( diff --git a/src/components/Toast/Toast.tsx b/src/components/Toast/Toast.tsx index 2faf7c4..a239790 100644 --- a/src/components/Toast/Toast.tsx +++ b/src/components/Toast/Toast.tsx @@ -5,12 +5,16 @@ import { Anchor, BackgroundColor, bgColorClass, + IconColor, + Radius, Shadow, TextColor, + textColorClass, Variant, } from "@/types"; import clsx from "clsx"; import { ToastContainer } from "@/components/ToastContainer"; +import radiusStyles from "@/styles/radius"; import shadowStyles from "@/styles/shadow"; export interface ToastProps extends Omit< @@ -27,10 +31,10 @@ export interface ToastProps extends Omit< } const variantStyles: Record = { - [Variant.Primary]: "text-content-text-primary", - [Variant.Secondary]: "text-content-text-secondary", - [Variant.Success]: "text-semantic-success", - [Variant.Danger]: "text-semantic-destructive", + [Variant.Primary]: textColorClass(TextColor.Primary), + [Variant.Secondary]: textColorClass(TextColor.Secondary), + [Variant.Success]: textColorClass(IconColor.Success), + [Variant.Danger]: textColorClass(IconColor.Destructive), }; export const Toast: FC = ({ @@ -51,7 +55,7 @@ export const Toast: FC = ({ "flex flex-nowrap", "gap-x-md", "p-4", - "rounded-md", + radiusStyles(Radius.Md), bgColorClass(BackgroundColor.Card2), shadowStyles(Shadow.Md), className diff --git a/src/components/Toggle/Toggle.tsx b/src/components/Toggle/Toggle.tsx index a0674ba..eb804de 100644 --- a/src/components/Toggle/Toggle.tsx +++ b/src/components/Toggle/Toggle.tsx @@ -1,9 +1,19 @@ -import radiusStyles from "@/styles/radius"; import { TEXT_STYLES } from "@/styles/text"; -import { Radius, Size, TextColor, textColorClass, TextVariant } from "@/types"; +import { + BackgroundColor, + bgColorClass, + BorderColor, + borderColorClass, + Radius, + Size, + TextColor, + textColorClass, + TextVariant, +} from "@/types"; import { cn } from "@/util/classes"; -import { Field, Switch as HeadlessSwitch, Label } from "@headlessui/react"; +import { Field, Label, Switch as HeadlessSwitch } from "@headlessui/react"; import { ButtonHTMLAttributes, type FC } from "react"; +import radiusStyles from "@/styles/radius"; type ModifiedToggleProps = Omit< ButtonHTMLAttributes, @@ -79,9 +89,9 @@ export const Toggle: FC = ({ "inline-flex", "cursor-pointer", "items-center", - "bg-content-bg-card-elevated", // TODO - evaluate if this intent is correct + bgColorClass(BackgroundColor.CardElevated), "border", - "border-content-border-secondary-primary", + borderColorClass(BorderColor.Default), "transition-colors", // when hovered "hover:bg-[#999999]", // TODO - current scheme doesn't have a light grey diff --git a/src/components/ToggleSwitch/ToggleSwitch.tsx b/src/components/ToggleSwitch/ToggleSwitch.tsx index 71d70ba..fe5569c 100644 --- a/src/components/ToggleSwitch/ToggleSwitch.tsx +++ b/src/components/ToggleSwitch/ToggleSwitch.tsx @@ -1,4 +1,14 @@ -import { Descriptor, Size, TextVariant } from "@/types"; +import { + BackgroundColor, + bgColorClass, + BrandColor, + Descriptor, + ElementState, + Size, + TextColor, + textColorClass, + TextVariant, +} from "@/types"; import { cn } from "@/util/classes"; import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@headlessui/react"; import clsx from "clsx"; @@ -56,19 +66,20 @@ const tabVariantStyles: Record = { "m-1", "py-1 px-1.5", "rounded-sm", - "data-[selected]:text-brand-accent" + textColorClass(BrandColor.Accent, ElementState.Selected) ), [ToggleSwitchVariant.Default]: clsx( - "data-[selected]:text-content-text-primary" + textColorClass(TextColor.Primary, ElementState.Selected) + ), + [ToggleSwitchVariant.Full]: clsx( + textColorClass(TextColor.Primary, ElementState.Selected) ), - [ToggleSwitchVariant.Full]: clsx("data-[selected]:text-content-text-primary"), [ToggleSwitchVariant.Borderless]: clsx( "bg-transparent", "hover:bg-transparent", "data-[selected]:bg-transparent", - "data-[selected]:text-content-text-primary", - "data-[selected]:border-b-2", - "data-[selected]:border-context-text-primary" + textColorClass(TextColor.Primary, ElementState.Selected), + "data-[selected]:border-b-2" ), }; @@ -116,7 +127,8 @@ export const ToggleSwitch: FC = ({ = ({ "flex-1", "flex items-center justify-center", "font-medium", - "text-content-text-secondary", + textColorClass(TextColor.Secondary), "outline-none", "transition-colors", - "hover:bg-content-bg-card-2", - "hover:text-content-text-primary", - "data-[selected]:bg-content-bg-card-2", + bgColorClass(BackgroundColor.Card2, ElementState.Hover), + textColorClass(TextColor.Primary, ElementState.Hover), + bgColorClass(BackgroundColor.Card2, ElementState.Selected), "data-[focus]:outline-none", getTabBorderRadius(variant, isFirst, isLast), getTabStyles(variant, size) diff --git a/src/styles/text.ts b/src/styles/text.ts index 23ea917..928e2a3 100644 --- a/src/styles/text.ts +++ b/src/styles/text.ts @@ -28,9 +28,9 @@ export const textColor = (variant: Variant): TextColor | undefined => { case Variant.Secondary: return TextColor.Secondary; case Variant.Success: - return TextColor.SemanticSuccess; + return TextColor.Success; case Variant.Danger: - return TextColor.SemanticDestructive; + return TextColor.Destructive; default: return TextColor.Primary; } diff --git a/src/theme/tokens/colors.ts b/src/theme/tokens/colors.ts index f96aae2..28488ba 100644 --- a/src/theme/tokens/colors.ts +++ b/src/theme/tokens/colors.ts @@ -5,68 +5,93 @@ export const colors = { accent: "#FF9950", }, semantic: { - success: "#047A48", - destructive: "#C53030", - info: "#2F5DF5", - warning: "#C27803", + success: "#1E7D45", + destructive: "#C33636", + info: "#2563EB", + warning: "#D97706", }, }, dark: { content: { bg: { - background: "#1A1A1A", + background: "#18191A", card: { - 1: "#1E1E1E", - 2: "#232323", - elevated: "#2D2D2D", + 1: "#1E1F20", + 2: "#232526", + elevated: "#2F3234", }, - muted: "#2A2A2A", - popover: "#181818", - secondary: "#2C2C2C", + muted: "#1F2021", + popover: "#141618", + secondary: "#1C1D1E", }, text: { - fg: "#FFF", + fg: "#FFF9F5", primary: "#FFF", - secondary: "#BBB", - tertiary: "#888", - muted: "#6E6E6E", + secondary: "#8F8D8B", + tertiary: "#6E6C6A", + muted: "#9C9896", + placeholder: "#6B6968", + success: "#1E7D45", + destructive: "#FF6767", + info: "#86B5F6", + warning: "#FCCB58", }, border: { - default: "#444", - strong: "#333", - hover: "#666", - focus: "#999", - secondary: { - primary: "#333", - secondary: "#555", - tertiary: "#333", - disabled: "#444", - }, + default: "#2F3234", + strong: "#3E4244", + hover: "#4A4C4E", + focus: "#6E6C6A", + subtle: "#1E1F20", + active: "#FF6D04", + error: "#FF6767", + success: "#7AB87C", + warning: "#FCCB58", + disabled: "#1E1F20", + }, + status: { + approved: "#7AB87C", + review: "#FF9950", + progress: "#CBA6FF", + default: "#8F8D8B", + failed: "#FF6767", + }, + icon: { + default: "#8F8D8B", + subtle: "#6E6C6A", + emphasis: "#FFF", + muted: "#6B6968", + disabled: "#6B6968", + decorative: "#FF9950", + brand: "#FF6D04", + success: "#7AB87C", + destructive: "#FF6767", + warning: "#FCCB58", + info: "#86B5F6", }, }, action: { primary: { - primary: "#FF6D05FF", - secondary: "#D05700FF", - tertiary: "#9C4100FF", + primary: "#FF6D04", + secondary: "#FF9950", + tertiary: "#D05700", text: "#FFF", }, secondary: { - primary: "transparent", - secondary: "transparent", - tertiary: "#222", - text: "#CCC", + primary: "#232526", + secondary: "#2A2B2C", + tertiary: "#1E1F20", + text: "#FFF", }, success: { - primary: "#047A48FF", - secondary: "#276749FF", - tertiary: "#1F563BFF", + primary: "#1E7D45", + secondary: "#7AB87C", + tertiary: "#166638", text: "#FFF", }, danger: { - primary: "#C53030FF", - secondary: "#9B2C2CFF", - tertiary: "#822727FF", + primary: "#C33636", + secondary: "#FF6767", + tertiary: "#9B2727", text: "#FFF", }, }, @@ -74,59 +99,84 @@ export const colors = { light: { content: { bg: { - background: "#F8F8F8", + background: "#F9F8F7", card: { 1: "#FFF", - 2: "#F5F5F5", - elevated: "#FAFAFA", + 2: "#F2F1F0", + elevated: "#FFF", }, - muted: "#F0F0F0", + muted: "#EEEDEC", popover: "#FFF", - secondary: "#F0F0F0", + secondary: "#E8E7E6", }, text: { fg: "#000", - primary: "#111", - secondary: "#555", - tertiary: "#777", - muted: "#999", + primary: "#18191A", + secondary: "#4A4847", + tertiary: "#6B6968", + muted: "#8F8D8B", + placeholder: "#B5B3B1", + success: "#166638", + destructive: "#9B2727", + info: "#1D4EBA", + warning: "#8A4A02", }, border: { - default: "#444", - strong: "#333", - hover: "#666", - focus: "#999", - secondary: { - primary: "#333", - secondary: "#555", - tertiary: "#333", - disabled: "#444", - }, + default: "#E0DEDC", + strong: "#D0CECC", + hover: "#C0BEBC", + focus: "#A0A09E", + subtle: "#EEEDEC", + active: "#FF6D04", + error: "#FF6767", + success: "#7AB87C", + warning: "#FCCB58", + disabled: "#1E1F20", + }, + status: { + approved: "#1E7D45", + review: "#D05700", + progress: "#8B5CF6", + default: "#6B6968", + failed: "#C33636", + }, + icon: { + default: "#6B6968", + subtle: "#8F8D8B", + emphasis: "#18191A", + muted: "#B5B3B1", + disabled: "#B5B3B1", + decorative: "#D05700", + brand: "#FF6D04", + success: "#1E7D45", + destructive: "#C33636", + warning: "#D97706", + info: "#2563EB", }, }, action: { primary: { - primary: "#FF6D05FF", - secondary: "#D05700FF", - tertiary: "#9C4100FF", + primary: "#FF6D05", + secondary: "#D05700", + tertiary: "#9B4200", text: "#FFF", }, secondary: { - primary: "#FFF", - secondary: "#FF6D05FF", - tertiary: "#9C4100FF", + primary: "#EEEDEC", + secondary: "#E0DEDC", + tertiary: "#D0CECC", text: "#000", }, success: { - primary: "#047A48FF", - secondary: "#276749FF", - tertiary: "#1F563BFF", + primary: "#1E7D45", + secondary: "#166638", + tertiary: "#0E4E2B", text: "#FFF", }, danger: { - primary: "#C53030FF", - secondary: "#9B2C2CFF", - tertiary: "#822727FF", + primary: "#C33636", + secondary: "#9B2727", + tertiary: "#751D1D", text: "#FFF", }, }, diff --git a/src/types/color.ts b/src/types/color.ts index 4aaef76..19400c8 100644 --- a/src/types/color.ts +++ b/src/types/color.ts @@ -1,58 +1,185 @@ -/** - * Tailwind classes for text colors - */ +import { ElementState, withElementState } from "@/types/element"; + +export enum ActionColor { + PrimaryDefault = "action-primary-primary", + PrimaryHover = "action-primary-secondary", + PrimaryFocus = "action-primary-tertiary", + PrimaryText = "action-primary-text", + SecondaryDefault = "action-secondary-primary", + SecondaryHover = "action-secondary-secondary", + SecondaryFocus = "action-secondary-tertiary", + SecondaryText = "action-secondary-text", + SuccessDefault = "action-success-primary", + SuccessHover = "action-success-secondary", + SuccessFocus = "action-success-tertiary", + SuccessText = "action-success-text", + DangerDefault = "action-danger-primary", + DangerHover = "action-danger-secondary", + DangerFocus = "action-danger-tertiary", + DangerText = "action-danger-text", +} + +export enum BackgroundColor { + Background = "bg-background", + Card1 = "bg-card-1", + Card2 = "bg-card-2", + CardElevated = "bg-card-elevated", + Muted = "bg-muted", + Popover = "bg-popover", + Secondary = "bg-secondary", +} + +export enum BorderColor { + Default = "border-default", + Strong = "border-strong", + Hover = "border-hover", + Focus = "border-focus", + Subtle = "border-subtle", + Active = "border-active", + Error = "border-error", + Success = "border-success", + Warning = "border-warning", + Disabled = "border-disabled", +} + +export enum BrandColor { + Primary = "brand-primary", + Accent = "brand-accent", +} + +export enum IconColor { + Default = "icon-default", + Subtle = "icon-subtle", + Emphasis = "icon-emphasis", + Muted = "icon-muted", + Disabled = "icon-disabled", + Decorative = "icon-decorative", + Brand = "icon-brand", + Success = "icon-success", + Destructive = "icon-destructive", + Warning = "icon-warning", + Info = "icon-info", +} + +export enum SemanticColor { + Success = "semantic-success", + Destructive = "semantic-destructive", + Info = "semantic-info", + Warning = "semantic-warning", +} + +export enum StatusColor { + Approved = "status-approved", + Review = "status-review", + Progress = "status-progress", + Default = "status-default", + Failed = "status-failed", +} + export enum TextColor { - Fg = "fg", - Primary = "primary", - Secondary = "secondary", - Tertiary = "tertiary", - Muted = "muted", - BrandPrimary = "brand-primary", - BrandAccent = "brand-accent", - SemanticSuccess = "semantic-success", - SemanticDestructive = "semantic-destructive", - SemanticInfo = "semantic-info", - SemanticWarning = "semantic-warning", + Fg = "text-fg", + Primary = "text-primary", + Secondary = "text-secondary", + Tertiary = "text-tertiary", + Muted = "text-muted", + Placeholder = "text-placeholder", + Success = "text-success", + Destructive = "text-destructive", + Warning = "text-warning", + Info = "text-info", } -const textColorMap: Record = { +export type Color = + | ActionColor + | BackgroundColor + | BrandColor + | IconColor + | SemanticColor + | StatusColor + | TextColor; + +const textColorMap: Record = { + [ActionColor.PrimaryDefault]: "text-action-primary-primary", + [ActionColor.PrimaryHover]: "text-action-primary-secondary", + [ActionColor.PrimaryFocus]: "text-action-primary-tertiary", + [ActionColor.PrimaryText]: "text-action-primary-text", + [ActionColor.SecondaryDefault]: "text-action-secondary-primary", + [ActionColor.SecondaryHover]: "text-action-secondary-secondary", + [ActionColor.SecondaryFocus]: "text-action-secondary-tertiary", + [ActionColor.SecondaryText]: "text-action-secondary-text", + [ActionColor.SuccessDefault]: "text-action-success-primary", + [ActionColor.SuccessHover]: "text-action-success-secondary", + [ActionColor.SuccessFocus]: "text-action-success-tertiary", + [ActionColor.SuccessText]: "text-action-success-text", + [ActionColor.DangerDefault]: "text-action-danger-primary", + [ActionColor.DangerHover]: "text-action-danger-secondary", + [ActionColor.DangerFocus]: "text-action-danger-tertiary", + [ActionColor.DangerText]: "text-action-danger-text", + + [BackgroundColor.Background]: "text-content-bg-background", + [BackgroundColor.Card1]: "text-content-bg-card-1", + [BackgroundColor.Card2]: "text-content-bg-card-2", + [BackgroundColor.CardElevated]: "text-content-bg-card-elevated", + [BackgroundColor.Muted]: "text-content-bg-muted", + [BackgroundColor.Popover]: "text-content-bg-popover", + [BackgroundColor.Secondary]: "text-content-bg-secondary", + + [BrandColor.Accent]: "text-brand-accent", + [BrandColor.Primary]: "text-brand-primary", + + [IconColor.Brand]: "text-content-icon-brand", + [IconColor.Decorative]: "text-content-icon-decorative", + [IconColor.Default]: "text-content-icon-default", + [IconColor.Destructive]: "text-content-icon-destructive", + [IconColor.Disabled]: "text-content-icon-disabled", + [IconColor.Emphasis]: "text-content-icon-emphasis", + [IconColor.Info]: "text-content-icon-info", + [IconColor.Muted]: "text-content-icon-muted", + [IconColor.Subtle]: "text-content-icon-subtle", + [IconColor.Success]: "text-content-icon-success", + [IconColor.Warning]: "text-content-icon-warning", + + [SemanticColor.Destructive]: "text-semantic-destructive", + [SemanticColor.Info]: "text-semantic-info", + [SemanticColor.Success]: "text-semantic-success", + [SemanticColor.Warning]: "text-semantic-warning", + + [StatusColor.Approved]: "text-content-status-approved", + [StatusColor.Default]: "text-content-status-default", + [StatusColor.Failed]: "text-content-status-failed", + [StatusColor.Progress]: "text-content-status-progress", + [StatusColor.Review]: "text-content-status-review", + + [TextColor.Destructive]: "text-content-text-destructive", [TextColor.Fg]: "text-content-text-fg", + [TextColor.Info]: "text-content-text-info", + [TextColor.Muted]: "text-content-text-muted", + [TextColor.Placeholder]: "text-content-text-placeholder", [TextColor.Primary]: "text-content-text-primary", [TextColor.Secondary]: "text-content-text-secondary", + [TextColor.Success]: "text-content-text-success", [TextColor.Tertiary]: "text-content-text-tertiary", - [TextColor.Muted]: "text-content-text-muted", - [TextColor.BrandPrimary]: "text-brand-primary", - [TextColor.BrandAccent]: "text-brand-accent", - [TextColor.SemanticSuccess]: "text-semantic-success", - [TextColor.SemanticDestructive]: "text-semantic-destructive", - [TextColor.SemanticInfo]: "text-semantic-info", - [TextColor.SemanticWarning]: "text-semantic-warning", + [TextColor.Warning]: "text-content-text-warning", }; -export function textColorClass(color: TextColor): string { - return textColorMap[color]; -} - -/** - * Tailwind classes for background colors - */ -export enum BackgroundColor { - Background = "background", - Card1 = "card-1", - Card2 = "card-2", - CardElevated = "card-elevated", - Muted = "muted", - Popover = "popover", - Secondary = "secondary", - BrandPrimary = "brand-primary", - BrandAccent = "brand-accent", - SemanticSuccess = "semantic-success", - SemanticDestructive = "semantic-destructive", - SemanticInfo = "semantic-info", - SemanticWarning = "semantic-warning", -} +const backgroundColorMap: Record = { + [ActionColor.PrimaryDefault]: "bg-action-primary-primary", + [ActionColor.PrimaryHover]: "bg-action-primary-secondary", + [ActionColor.PrimaryFocus]: "bg-action-primary-tertiary", + [ActionColor.PrimaryText]: "bg-action-primary-text", + [ActionColor.SecondaryDefault]: "bg-action-secondary-primary", + [ActionColor.SecondaryHover]: "bg-action-secondary-secondary", + [ActionColor.SecondaryFocus]: "bg-action-secondary-tertiary", + [ActionColor.SecondaryText]: "bg-action-secondary-text", + [ActionColor.SuccessDefault]: "bg-action-success-primary", + [ActionColor.SuccessHover]: "bg-action-success-secondary", + [ActionColor.SuccessFocus]: "bg-action-success-tertiary", + [ActionColor.SuccessText]: "bg-action-success-text", + [ActionColor.DangerDefault]: "bg-action-danger-primary", + [ActionColor.DangerHover]: "bg-action-danger-secondary", + [ActionColor.DangerFocus]: "bg-action-danger-tertiary", + [ActionColor.DangerText]: "bg-action-danger-text", -const backgroundColorMap: Record = { [BackgroundColor.Background]: "bg-content-bg-background", [BackgroundColor.Card1]: "bg-content-bg-card-1", [BackgroundColor.Card2]: "bg-content-bg-card-2", @@ -60,14 +187,106 @@ const backgroundColorMap: Record = { [BackgroundColor.Muted]: "bg-content-bg-muted", [BackgroundColor.Popover]: "bg-content-bg-popover", [BackgroundColor.Secondary]: "bg-content-bg-secondary", - [BackgroundColor.BrandPrimary]: "bg-brand-primary", - [BackgroundColor.BrandAccent]: "bg-brand-accent", - [BackgroundColor.SemanticSuccess]: "bg-semantic-success", - [BackgroundColor.SemanticDestructive]: "bg-semantic-destructive", - [BackgroundColor.SemanticInfo]: "bg-semantic-info", - [BackgroundColor.SemanticWarning]: "bg-semantic-warning", + + [BrandColor.Accent]: "bg-brand-accent", + [BrandColor.Primary]: "bg-brand-primary", + + [IconColor.Brand]: "bg-content-icon-brand", + [IconColor.Decorative]: "bg-content-icon-decorative", + [IconColor.Default]: "bg-content-icon-default", + [IconColor.Destructive]: "bg-content-icon-destructive", + [IconColor.Disabled]: "bg-content-icon-disabled", + [IconColor.Emphasis]: "bg-content-icon-emphasis", + [IconColor.Info]: "bg-content-icon-info", + [IconColor.Muted]: "bg-content-icon-muted", + [IconColor.Subtle]: "bg-content-icon-subtle", + [IconColor.Success]: "bg-content-icon-success", + [IconColor.Warning]: "bg-content-icon-warning", + + [SemanticColor.Destructive]: "bg-semantic-destructive", + [SemanticColor.Info]: "bg-semantic-info", + [SemanticColor.Success]: "bg-semantic-success", + [SemanticColor.Warning]: "bg-semantic-warning", + + [StatusColor.Approved]: "bg-content-status-approved", + [StatusColor.Default]: "bg-content-status-default", + [StatusColor.Failed]: "bg-content-status-failed", + [StatusColor.Progress]: "bg-content-status-progress", + [StatusColor.Review]: "bg-content-status-review", + + [TextColor.Destructive]: "bg-content-text-destructive", + [TextColor.Fg]: "bg-content-text-fg", + [TextColor.Info]: "bg-content-text-info", + [TextColor.Muted]: "bg-content-text-muted", + [TextColor.Placeholder]: "bg-content-text-placeholder", + [TextColor.Primary]: "bg-content-text-primary", + [TextColor.Secondary]: "bg-content-text-secondary", + [TextColor.Success]: "bg-content-text-success", + [TextColor.Tertiary]: "bg-content-text-tertiary", + [TextColor.Warning]: "bg-content-text-warning", }; -export function bgColorClass(color: BackgroundColor): string { - return backgroundColorMap[color]; -} +const borderColorMap: Record = { + [BorderColor.Active]: "border-content-border-active", + [BorderColor.Default]: "border-content-border-default", + [BorderColor.Disabled]: "border-content-border-disabled", + [BorderColor.Error]: "border-content-border-error", + [BorderColor.Focus]: "border-content-border-focus", + [BorderColor.Hover]: "border-content-border-hover", + [BorderColor.Strong]: "border-content-border-strong", + [BorderColor.Subtle]: "border-content-border-subtle", + [BorderColor.Success]: "border-content-border-success", + [BorderColor.Warning]: "border-content-border-warning", +}; + +export const bgColorClass = ( + color: Color, + elementState: ElementState = ElementState.None +): string => { + if (elementState === ElementState.None) { + return backgroundColorMap[color]; + } + + return withElementState(`bg-(${getColorCssVar(color)})`, elementState); +}; + +const getColorCssVar = (color: Color | BorderColor): string => { + if ( + isEnumValue(color, BrandColor) || + isEnumValue(color, SemanticColor) || + isEnumValue(color, ActionColor) + ) { + return `--color-${color}`; + } + + return `--color-content-${color}`; +}; + +const isEnumValue = >( + value: unknown, + enumType: T +): value is T[keyof T] => { + return Object.values(enumType).includes(value as string); +}; + +export const borderColorClass = ( + color: BorderColor, + elementState: ElementState = ElementState.None +): string => { + if (elementState === ElementState.None) { + return borderColorMap[color]; + } + + return withElementState(`border-(${getColorCssVar(color)})`, elementState); +}; + +export const textColorClass = ( + color: Color, + elementState: ElementState = ElementState.None +): string => { + if (elementState === ElementState.None) { + return textColorMap[color]; + } + + return withElementState(`text-(${getColorCssVar(color)})`, elementState); +}; diff --git a/src/types/element.ts b/src/types/element.ts new file mode 100644 index 0000000..c5be5ae --- /dev/null +++ b/src/types/element.ts @@ -0,0 +1,22 @@ +export enum ElementState { + Active = "data-active", + AutoFocus = "data-autofocus", + Checked = "data-checked", + Disabled = "disabled", + Focus = "data-focus", + Hover = "hover", + None = "none", + Open = "data-open", + Selected = "data-selected", +} + +export const withElementState = ( + cssClass: string, + state: ElementState = ElementState.None +): string => { + if (state === ElementState.None) { + return cssClass; + } + + return `${state}:${cssClass}`; +}; diff --git a/src/types/index.ts b/src/types/index.ts index c68470b..7beee0c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,6 +1,7 @@ export * from "./anchor"; export * from "./color"; export * from "./descriptor"; +export * from "./element"; export * from "./heading"; export * from "./orientation"; export * from "./radius";