diff --git a/assets/design-system/src/GlobalStyle.tsx b/assets/design-system/src/GlobalStyle.tsx index 6d5231f3db..765b85e252 100644 --- a/assets/design-system/src/GlobalStyle.tsx +++ b/assets/design-system/src/GlobalStyle.tsx @@ -132,6 +132,11 @@ const GlobalStyle = createGlobalStyle(({ theme }) => ({ [lightModeSelectors]: { ...getSemanticColorCSSVars({ mode: 'light' }), }, + // Keep html/body on the app-shell token (index.html reads --color-page-background). + // This is intentionally NOT fill-zero. + 'html, body': { + backgroundColor: theme.colors['page-background'], + }, '*': theme.partials.scrollBar({ fillLevel: 0 }), })) diff --git a/assets/design-system/src/components/AppIcon.tsx b/assets/design-system/src/components/AppIcon.tsx index 64a70a11c1..ff032580b9 100644 --- a/assets/design-system/src/components/AppIcon.tsx +++ b/assets/design-system/src/components/AppIcon.tsx @@ -19,6 +19,7 @@ type AppIconProps = { spacing?: AppIconSpacing hue?: AppIconHue clickable?: boolean + rounded?: boolean url?: Nullable icon?: ReactElement alt?: Nullable @@ -97,22 +98,33 @@ const AppIconSC = styled.div<{ $hasBorder?: boolean $boxSize?: number $clickable?: boolean -}>(({ theme, $color, $borderColor, $hasBorder, $boxSize, $clickable }) => ({ - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - backgroundColor: $color ? theme.colors[$color] : undefined, - borderRadius: theme.borderRadiuses.medium, - border: $hasBorder ? theme.borders.default : 'none', - borderColor: $borderColor ? theme.colors[$borderColor] : undefined, - width: $boxSize, - height: $boxSize, - minWidth: $boxSize, - minHeight: $boxSize, - cursor: $clickable ? 'pointer' : 'auto', - overflow: 'hidden', - _hover: $clickable ? { backgroundColor: $borderColor } : undefined, -})) + $rounded?: boolean +}>( + ({ + theme, + $color, + $borderColor, + $hasBorder, + $boxSize, + $clickable, + $rounded, + }) => ({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: $color ? theme.colors[$color] : undefined, + borderRadius: $rounded ? '50%' : theme.borderRadiuses.medium, + border: $hasBorder ? theme.borders.default : 'none', + borderColor: $borderColor ? theme.colors[$borderColor] : undefined, + width: $boxSize, + height: $boxSize, + minWidth: $boxSize, + minHeight: $boxSize, + cursor: $clickable ? 'pointer' : 'auto', + overflow: 'hidden', + _hover: $clickable ? { backgroundColor: $borderColor } : undefined, + }) +) const InitialsSC = styled.div<{ $size: AppIconSize @@ -140,6 +152,7 @@ function AppIcon({ spacing = 'padding', hue, clickable = false, + rounded = false, url, icon, alt, @@ -175,6 +188,7 @@ function AppIcon({ $hasBorder={hasBorder} $boxSize={boxSize} $clickable={clickable} + $rounded={rounded} onClick={clickable ? onClose : undefined} {...props} > diff --git a/assets/design-system/src/components/Button.tsx b/assets/design-system/src/components/Button.tsx index b420ac5aa9..56669da2f3 100644 --- a/assets/design-system/src/components/Button.tsx +++ b/assets/design-system/src/components/Button.tsx @@ -12,6 +12,7 @@ import { import { CSSProperties, styled, useTheme } from 'styled-components' import type { RelativeRoutingType } from 'react-router-dom' import { resolveSpacersAndSanitizeCss, SpacerProps } from '../theme/spacing' +import { lightElevatedSurface } from '../theme/lightElevatedSurface' import { applyNodeToRefs } from '../utils/applyNodeToRefs' import Flex, { FlexProps } from './Flex' import { Spinner } from './Spinner' @@ -188,7 +189,8 @@ export const ButtonBaseSC = styled.button<{ $noPadding: boolean }>( ({ - theme: { colors, spacing, partials, borderRadiuses, boxShadows }, + theme, + theme: { colors, spacing, partials, borderRadiuses, boxShadows, mode }, $size, $type, $noPadding, @@ -229,22 +231,34 @@ export const ButtonBaseSC = styled.button<{ // secondary styles ...($type === 'secondary' && { color: colors['text-light'], - background: 'transparent', + background: mode === 'light' ? colors['fill-zero'] : 'transparent', borderColor: colors['border-input'], + ...lightElevatedSurface(theme), '&:hover': { color: colors['text'], - background: colors['action-input-hover'], + background: + mode === 'light' + ? colors['fill-zero-hover'] + : colors['action-input-hover'], borderColor: colors['border-input'], }, - '&:active': { color: colors['text'], background: 'transparent' }, + '&:active': { + color: colors['text'], + background: mode === 'light' ? colors['fill-zero'] : 'transparent', + }, '&:focus-visible': { color: colors['text'], - background: colors['action-input-hover'], + background: + mode === 'light' + ? colors['fill-zero-hover'] + : colors['action-input-hover'], + boxShadow: 'none', }, '&:disabled': { cursor: 'not-allowed', color: colors['text-disabled'], - background: 'transparent', + background: mode === 'light' ? colors['fill-zero'] : 'transparent', + boxShadow: 'none', }, }), // tertiary styles @@ -295,32 +309,42 @@ export const ButtonBaseSC = styled.button<{ // floating styles ...($type === 'floating' && { color: colors['text-light'], - background: colors['fill-two'], + background: mode === 'light' ? colors['fill-zero'] : colors['fill-two'], borderColor: colors['border-input'], - boxShadow: boxShadows.slight, + ...lightElevatedSurface(theme), + ...(mode !== 'light' ? { boxShadow: boxShadows.slight } : {}), '&:hover': { color: colors['text'], - background: colors['fill-two'], + background: mode === 'light' ? colors['fill-zero'] : colors['fill-two'], borderColor: colors['border-input'], boxShadow: boxShadows.moderate, }, '&:active': { color: colors['text'], - background: colors['fill-two-hover'], + background: + mode === 'light' + ? colors['fill-zero-hover'] + : colors['fill-two-hover'], borderColor: colors['border-input'], }, '&:focus-visible': { color: colors['text'], - background: colors['fill-two-selected'], + background: + mode === 'light' + ? colors['fill-zero-selected'] + : colors['fill-two-selected'], + boxShadow: 'none', }, '&:disabled': { cursor: 'not-allowed', color: colors['text-disabled'], - borderColor: colors['border-input'], - background: 'transparent', + borderColor: mode === 'light' ? 'transparent' : colors['border-input'], + background: mode === 'light' ? colors['fill-zero'] : 'transparent', + boxShadow: 'none', '&:hover': { - borderColor: colors['border-input'], - background: 'transparent', + borderColor: + mode === 'light' ? 'transparent' : colors['border-input'], + background: mode === 'light' ? colors['fill-zero'] : 'transparent', }, }, }), diff --git a/assets/design-system/src/components/Card.tsx b/assets/design-system/src/components/Card.tsx index c8c7a94ea9..9d2f1e3b1c 100644 --- a/assets/design-system/src/components/Card.tsx +++ b/assets/design-system/src/components/Card.tsx @@ -9,6 +9,8 @@ import { useFillLevel, } from './contexts/FillLevelContext' import WrapWithIf from './WrapWithIf' +import { lightElevatedSurface } from '../theme/lightElevatedSurface' +import { borderWidths } from '../theme/borders' type CornerSize = 'medium' | 'large' type CardFillLevel = Exclude @@ -99,7 +101,10 @@ const HeaderSC = styled.div<{ ], height: size === 'large' ? 48 : 40, padding: `0 ${theme.spacing.medium}px`, - overflow: 'hidden', + // overflow:hidden + matching radius shears the 1px border at corners + // and can make the white fill look clipped against the border curve. + overflow: theme.mode === 'light' ? 'visible' : 'hidden', + ...(theme.mode === 'light' && { backgroundClip: 'padding-box' }), }) ) @@ -111,6 +116,7 @@ const CardSC = styled(Div)<{ $selected: boolean $clickable: boolean $disabled: boolean + $overflowSpecified: boolean }>( ({ theme, @@ -121,6 +127,7 @@ const CardSC = styled(Div)<{ $selected: selected, $clickable: clickable, $disabled: disabled, + $overflowSpecified, }) => ({ ...theme.partials.reset.button, border: `1px solid ${ @@ -133,6 +140,18 @@ const CardSC = styled(Div)<{ borderRadius: $hasHeader ? `0 0 ${theme.borderRadiuses[cornerSize]}px ${theme.borderRadiuses[cornerSize]}px` : theme.borderRadiuses[cornerSize], + // Soft lift on the body when there's no header wrapper; header cards + // elevate via OuterWrapSC so the full card (header + body) casts one shadow. + ...(!$hasHeader ? lightElevatedSurface(theme) : null), + // Soft box-shadow paints outside the border box; hidden clips it flush. + // Skip when the caller set overflow so scrollable cards (e.g. stack logs) + // can still clip and scroll. + ...(theme.mode === 'light' && + !$overflowSpecified && { overflow: 'visible' }), + // Keep opaque fill inset from the border curve (outer radius − border width) + ...(theme.mode === 'light' && { + backgroundClip: 'padding-box', + }), ...($hasTabs && { borderTopLeftRadius: 0, // TODO: It should be applied only if first tab is active. }), @@ -163,15 +182,28 @@ const CardSC = styled(Div)<{ }) ) -const OuterWrapSC = styled.div<{ $overflowVisible: boolean }>( - ({ $overflowVisible: overflowVisible }) => ({ +const OuterWrapSC = styled.div<{ + $overflowVisible: boolean + $cornerSize: CornerSize +}>(({ theme, $overflowVisible: overflowVisible, $cornerSize: cornerSize }) => { + const outerRadius = theme.borderRadiuses[cornerSize] + // Inner white/header pieces use outerRadius; shadow host uses outer+border + // so the curve isn’t flush with the opaque fill (reads as a hard clip). + const shadowRadius = outerRadius + borderWidths.default + + return { display: 'flex', flexDirection: 'column', - overflow: overflowVisible ? 'visible' : 'hidden', + // Light mode cards use box-shadow; overflow:hidden clips it on all sides. + overflow: overflowVisible || theme.mode === 'light' ? 'visible' : 'hidden', width: '100%', height: '100%', - }) -) + ...(theme.mode === 'light' && { + borderRadius: shadowRadius, + boxShadow: theme.boxShadows.slight, + }), + } +}) function Card({ ref, @@ -184,11 +216,16 @@ function Card({ clickable = false, disabled = false, children, + overflow, + overflowX, + overflowY, ...props }: CardProps) { const hasHeader = !!header const hasTabs = !!tabs const { size, content: headerContent, headerProps, outerProps } = header ?? {} + const overflowSpecified = + overflow != null || overflowX != null || overflowY != null const mainFillLevel = useDecideFillLevel({ fillLevel }) const headerFillLevel = useDecideFillLevel({ fillLevel: mainFillLevel + 1 }) @@ -200,6 +237,7 @@ function Card({ wrapper={ } @@ -230,6 +268,10 @@ function Card({ 'data-clickable': 'true', })} $disabled={clickable && disabled} + $overflowSpecified={overflowSpecified} + overflow={overflow} + overflowX={overflowX} + overflowY={overflowY} {...props} > {children} diff --git a/assets/design-system/src/components/Chip.tsx b/assets/design-system/src/components/Chip.tsx index b34607c6c0..7b85928d4c 100644 --- a/assets/design-system/src/components/Chip.tsx +++ b/assets/design-system/src/components/Chip.tsx @@ -106,6 +106,8 @@ const ChipCardSC = styled(Card)<{ display: 'inline-flex', textDecoration: 'none', gap: $condensed ? 6 : theme.spacing.xsmall, + // Chips are dense inline labels — hairline only, no Card elevation shadow + ...(theme.mode === 'light' && { boxShadow: 'none' }), }, '.children': { display: 'flex', @@ -231,7 +233,7 @@ function Chip({ {...{ [CHIP_CLOSE_ATTR_KEY]: '', }} - as={clickable ? 'div' : 'button'} + {...(clickable ? { as: 'div' } : {})} {...(closeButtonProps || {})} > & { @@ -83,16 +83,20 @@ function CodeHeaderUnstyled({ const CodeHeader = styled(CodeHeaderUnstyled)<{ $visuallyHidden?: boolean }>( ({ $visuallyHidden = false, fillLevel, theme }) => ({ - minHeight: theme.spacing.xlarge + theme.spacing.xsmall * 2, - padding: `${theme.spacing.xsmall}px ${theme.spacing.medium}px`, + minHeight: theme.spacing.large, + padding: `${theme.spacing.xsmall}px ${theme.spacing.small}px`, borderBottom: fillLevel >= 1 ? theme.borders['fill-three'] : theme.borders['fill-two'], backgroundColor: - fillLevel >= 1 ? theme.colors['fill-three'] : theme.colors['fill-two'], + theme.mode === 'light' + ? theme.colors['fill-one'] + : fillLevel >= 1 + ? theme.colors['fill-three'] + : theme.colors['fill-two'], display: 'flex', alignItems: 'center', justifyContent: 'space-between', - gap: theme.spacing.medium, + gap: theme.spacing.xsmall, borderTopLeftRadius: theme.borderRadiuses.medium + 2, borderTopRightRadius: theme.borderRadiuses.medium + 2, ...($visuallyHidden @@ -153,10 +157,11 @@ type CodeTabData = { const TitleArea = styled.div<{ $shrinkable: boolean; $preserveCase?: boolean }>( ({ $shrinkable, $preserveCase = false, theme }) => ({ display: 'flex', + alignItems: 'center', flexShrink: $shrinkable ? 1 : 0, flexGrow: 1, - gap: theme.spacing.xsmall, - ...theme.partials.text.overline, + gap: theme.spacing.xxsmall, + ...theme.partials.text.caption, color: 'text-light', ...($preserveCase ? { textTransform: 'none' } : {}), }) @@ -384,8 +389,8 @@ const CodeContentSC = styled.div<{ height: '100%', overflow: 'auto', alignItems: 'center', - padding: `${$multiLine ? theme.spacing.medium : theme.spacing.small}px ${ - theme.spacing.medium + padding: `${$multiLine ? theme.spacing.small : theme.spacing.xsmall}px ${ + theme.spacing.small }px`, borderBottomLeftRadius: theme.borderRadiuses.large, borderBottomRightRadius: theme.borderRadiuses.large, @@ -444,13 +449,17 @@ function CodeUnstyled({ [onSelectedTabChange, selectedTabKey, tabInterface, setTabInterface, tabs] ) + const selectedTabLanguage = tabs?.find( + (tab) => tab.key === selectedTabKey + )?.language + const titleArea = (tabs && title) || !tabs ? ( - + {getCodeLanguageIcon(language || selectedTabLanguage)} {(title || language) &&
{title || language}
}
) : undefined @@ -458,7 +467,11 @@ function CodeUnstyled({ const content = ( = 1 ? theme.colors['border-fill-three'] diff --git a/assets/design-system/src/components/IconFrame.tsx b/assets/design-system/src/components/IconFrame.tsx index c68bab9824..94c2adad95 100644 --- a/assets/design-system/src/components/IconFrame.tsx +++ b/assets/design-system/src/components/IconFrame.tsx @@ -7,6 +7,7 @@ import { import styled, { DefaultTheme } from 'styled-components' import { type SemanticColorKey } from '../theme/colors' +import { lightElevatedSurface } from '../theme/lightElevatedSurface' import { type PolymorphicComponentProps } from '../types' @@ -21,7 +22,7 @@ function typeToBG(theme: DefaultTheme): Record { tertiary: 'transparent', floating: theme.mode === 'light' - ? theme.colors['fill-three'] + ? theme.colors['fill-zero'] : theme.colors['fill-two'], } } @@ -32,7 +33,7 @@ function typeToHoverBG(theme: DefaultTheme): Record { tertiary: theme.colors['action-input-hover'], floating: theme.mode === 'light' - ? theme.colors['fill-three-hover'] + ? theme.colors['fill-zero-hover'] : theme.colors['fill-two-hover'], } } @@ -45,7 +46,7 @@ function typeToSelectedBG( tertiary: undefined, floating: theme.mode === 'light' - ? theme.colors['fill-three-selected'] + ? theme.colors['fill-zero-selected'] : theme.colors['fill-two-selected'], } } @@ -56,7 +57,7 @@ function typeToFocusBG(theme: DefaultTheme): Record { tertiary: undefined, floating: theme.mode === 'light' - ? theme.colors['fill-three-selected'] + ? theme.colors['fill-zero-selected'] : theme.colors['fill-two-selected'], } } @@ -67,7 +68,7 @@ function typeToBorder(theme: DefaultTheme): Record { theme.mode === 'light' ? theme.borders['fill-two'] : theme.borders.input, tertiary: '1px solid transparent', floating: - theme.mode === 'light' ? theme.borders['fill-two'] : theme.borders.input, + theme.mode === 'light' ? '1px solid transparent' : theme.borders.input, } } @@ -157,7 +158,9 @@ const IconFrameSC = styled.div<{ }, } : {}), - ...($type === 'floating' ? { boxShadow: theme.boxShadows.slight } : {}), + ...($type === 'floating' && lightElevatedSurface(theme)), + ...($type === 'floating' && + theme.mode !== 'light' && { boxShadow: theme.boxShadows.slight }), })) function IconFrame({ diff --git a/assets/design-system/src/components/InlineCode.tsx b/assets/design-system/src/components/InlineCode.tsx index c284cbb093..a9d6fd8d6e 100644 --- a/assets/design-system/src/components/InlineCode.tsx +++ b/assets/design-system/src/components/InlineCode.tsx @@ -52,7 +52,10 @@ const CodeElt = styled.code<{ $parentFillLevel: FillLevel }>( borderColor: (theme.colors as any)[ parentFillLevelToBorderColor[parentFillLevel] ], - backgroundColor: theme.colors['fill-one'], + backgroundColor: + theme.mode === 'light' + ? theme.colors['fill-two'] + : theme.colors['fill-one'], 'a:any-link &': { color: theme.colors['action-link-inline'], }, diff --git a/assets/design-system/src/components/Input2.tsx b/assets/design-system/src/components/Input2.tsx index d06895c7e3..7e0cabfb30 100644 --- a/assets/design-system/src/components/Input2.tsx +++ b/assets/design-system/src/components/Input2.tsx @@ -22,6 +22,7 @@ import { parentFillLevelToBackground, TitleContent } from './Select' import Tooltip from './Tooltip' import { useFormField } from './FormField' +import { lightElevatedSurface } from '../theme/lightElevatedSurface' export type InputProps = { suffix?: ReactNode @@ -126,7 +127,7 @@ const InputRootSC = styled.div<{ ? theme.partials.text.caption : theme.partials.text.body2), display: 'flex', - overflow: 'hidden', + overflow: theme.mode === 'light' ? 'visible' : 'hidden', justifyContent: 'space-between', alignItems: 'center', height: 'auto', @@ -142,11 +143,14 @@ const InputRootSC = styled.div<{ ? theme.colors['border-danger'] : theme.colors['border-input'], borderRadius: theme.borderRadiuses.medium, + ...lightElevatedSurface(theme, { error: $error }), '&:focus-within': { borderColor: theme.colors['border-outline-focused'], + boxShadow: 'none', }, '&[aria-disabled=true]': { borderColor: theme.colors['border-disabled'], + boxShadow: 'none', }, '&[aria-disabled=true], &[aria-disabled=true] *': { color: theme.colors['text-input-disabled'], diff --git a/assets/design-system/src/components/ListBox.tsx b/assets/design-system/src/components/ListBox.tsx index d543369cc0..07373da0e4 100644 --- a/assets/design-system/src/components/ListBox.tsx +++ b/assets/design-system/src/components/ListBox.tsx @@ -60,6 +60,7 @@ const ListBoxCard = styled(Card)(({ theme }) => ({ flexShrink: 1, overflowX: 'visible', overflowY: 'hidden', + // White floating panel — match notifications / header menus background: theme.mode === 'light' ? theme.colors['fill-zero'] diff --git a/assets/design-system/src/components/Markdown.tsx b/assets/design-system/src/components/Markdown.tsx index 6d589b6bbf..87b14170c0 100644 --- a/assets/design-system/src/components/Markdown.tsx +++ b/assets/design-system/src/components/Markdown.tsx @@ -240,7 +240,10 @@ const MdTh = styled.th(({ theme }) => ({ })) const MdTd = styled.td(({ theme }) => ({ - backgroundColor: theme.colors['fill-zero-selected'], + backgroundColor: + theme.mode === 'light' + ? theme.colors['fill-one'] + : theme.colors['fill-zero-selected'], padding: `${theme.spacing.xsmall}px ${theme.spacing.small}px`, color: theme.colors['text-light'], height: 40, diff --git a/assets/design-system/src/components/PopoverListBox.tsx b/assets/design-system/src/components/PopoverListBox.tsx index 20cb8629fd..d7f42166a5 100644 --- a/assets/design-system/src/components/PopoverListBox.tsx +++ b/assets/design-system/src/components/PopoverListBox.tsx @@ -137,7 +137,7 @@ function PopoverListBox({ headerFixed={dropdownHeaderFixed} footerFixed={dropdownFooterFixed} extendStyle={{ - boxShadow: theme.boxShadows.moderate, + boxShadow: theme.boxShadows.slight, }} listBoxRef={listBoxRef} {...listBoxProps} diff --git a/assets/design-system/src/components/Select.tsx b/assets/design-system/src/components/Select.tsx index 9c4e2c9247..efc4696ebe 100644 --- a/assets/design-system/src/components/Select.tsx +++ b/assets/design-system/src/components/Select.tsx @@ -32,6 +32,7 @@ import { } from './SelectComboShared' import { type FillLevel, useFillLevel } from './contexts/FillLevelContext' import CaretDownIcon from './icons/CaretDownIcon' +import { lightElevatedSurface } from '../theme/lightElevatedSurface' export const parentFillLevelToBackground = { 0: 'fill-one', @@ -158,7 +159,10 @@ const SelectButtonInner = styled.div<{ color: theme.colors['text-light'], border: theme.borders.input, borderRadius: theme.borderRadiuses.medium, - overflow: 'hidden', + // Soft shadow needs to paint outside the control in light mode + overflow: theme.mode === 'light' ? 'visible' : 'hidden', + ...(theme.mode === 'light' && { backgroundClip: 'padding-box' }), + ...lightElevatedSurface(theme, { disabled: isDisabled }), '.content': { alignItems: 'center', display: 'flex', @@ -189,6 +193,7 @@ const SelectButtonInner = styled.div<{ }, '&:focus-visible': { ...theme.partials.focus.default, + boxShadow: 'none', }, '&:hover': { color: theme.colors.text, @@ -196,6 +201,7 @@ const SelectButtonInner = styled.div<{ }, ...(isDisabled && { borderColor: theme.colors['border-disabled'], + boxShadow: 'none', color: theme.colors['text-input-disabled'], cursor: 'not-allowed', diff --git a/assets/design-system/src/components/Sidecar.tsx b/assets/design-system/src/components/Sidecar.tsx index a7a5e2b8a4..3f2a979302 100644 --- a/assets/design-system/src/components/Sidecar.tsx +++ b/assets/design-system/src/components/Sidecar.tsx @@ -12,6 +12,11 @@ const SidecarSC = styled(Section)(({ theme }) => ({ border: theme.borders.default, borderRadius: theme.borderRadiuses.medium, padding: theme.spacing.medium, + // Light: white metadata panel on the page canvas; dark: raised nested surface + backgroundColor: + theme.mode === 'light' + ? theme.colors['fill-zero'] + : theme.colors['fill-two'], })) const SidecarHeadingSC = styled.h1(({ theme }) => ({ ...theme.partials.text.overline, diff --git a/assets/design-system/src/components/Tab.tsx b/assets/design-system/src/components/Tab.tsx index 4f8dd72f9b..803710e9be 100644 --- a/assets/design-system/src/components/Tab.tsx +++ b/assets/design-system/src/components/Tab.tsx @@ -95,14 +95,24 @@ function Tab({ : theme.colors['text-xlight'] } backgroundColor={ - !active && activeSecondary ? theme.colors['fill-two'] : 'transparent' + theme.mode === 'light' + ? active + ? theme.colors['fill-zero-selected'] + : activeSecondary + ? theme.colors['fill-zero-hover'] + : 'transparent' + : !active && activeSecondary + ? theme.colors['fill-two'] + : 'transparent' } {...{ '&:hover': { color: theme.colors.text, - ...(!(!active && activeSecondary) + ...(theme.mode === 'light' ? { backgroundColor: theme.colors['fill-zero-hover'] } - : {}), + : !(!active && activeSecondary) + ? { backgroundColor: theme.colors['fill-zero-hover'] } + : {}), }, }} transition="background-color 150ms ease, border-color 150ms ease, color 150ms ease" diff --git a/assets/design-system/src/components/codeLanguageIcons.tsx b/assets/design-system/src/components/codeLanguageIcons.tsx new file mode 100644 index 0000000000..22d307631e --- /dev/null +++ b/assets/design-system/src/components/codeLanguageIcons.tsx @@ -0,0 +1,70 @@ +import { type ReactNode } from 'react' + +import type { IconProps } from './icons/createIcon' +import CppLogoIcon from './icons/CppLogoIcon' +import CSharpLogoIcon from './icons/CSharpLogoIcon' +import ElixirLogoIcon from './icons/ElixirLogoIcon' +import GoLogoIcon from './icons/GoLogoIcon' +import JavaLogoIcon from './icons/JavaLogoIcon' +import JavaScriptLogoIcon from './icons/JavaScriptLogoIcon' +import PhpLogoIcon from './icons/PhpLogoIcon' +import PythonLogoIcon from './icons/PythonLogoIcon' +import RubyLogoIcon from './icons/RubyLogoIcon' +import RustLogoIcon from './icons/RustLogoIcon' +import ScalaLogoIcon from './icons/ScalaLogoIcon' +import TerraformLogoIcon from './icons/TerraformLogoIcon' +import TypeScriptLogoIcon from './icons/TypeScriptLogoIcon' +import YamlLogoIcon from './icons/YamlLogoIcon' + +const LANGUAGE_ICONS: Record ReactNode> = { + yaml: YamlLogoIcon, + yml: YamlLogoIcon, + terraform: TerraformLogoIcon, + tf: TerraformLogoIcon, + hcl: TerraformLogoIcon, + go: GoLogoIcon, + golang: GoLogoIcon, + python: PythonLogoIcon, + py: PythonLogoIcon, + rust: RustLogoIcon, + rs: RustLogoIcon, + javascript: JavaScriptLogoIcon, + js: JavaScriptLogoIcon, + jsx: JavaScriptLogoIcon, + mjs: JavaScriptLogoIcon, + cjs: JavaScriptLogoIcon, + typescript: TypeScriptLogoIcon, + ts: TypeScriptLogoIcon, + tsx: TypeScriptLogoIcon, + mts: TypeScriptLogoIcon, + cts: TypeScriptLogoIcon, + java: JavaLogoIcon, + csharp: CSharpLogoIcon, + cs: CSharpLogoIcon, + 'c#': CSharpLogoIcon, + cpp: CppLogoIcon, + 'c++': CppLogoIcon, + cc: CppLogoIcon, + cxx: CppLogoIcon, + hpp: CppLogoIcon, + hh: CppLogoIcon, + hxx: CppLogoIcon, + elixir: ElixirLogoIcon, + ex: ElixirLogoIcon, + exs: ElixirLogoIcon, + ruby: RubyLogoIcon, + rb: RubyLogoIcon, + php: PhpLogoIcon, + scala: ScalaLogoIcon, +} + +export function getCodeLanguageIcon( + language?: string, + props: IconProps = { size: 12, fullColor: true } +) { + if (!language) return null + + const Icon = LANGUAGE_ICONS[language.toLowerCase().trim()] + + return Icon ? : null +} diff --git a/assets/design-system/src/components/icons/AnsibleIcon.tsx b/assets/design-system/src/components/icons/AnsibleIcon.tsx index 9215baf25b..2a2d7c0976 100644 --- a/assets/design-system/src/components/icons/AnsibleIcon.tsx +++ b/assets/design-system/src/components/icons/AnsibleIcon.tsx @@ -1,13 +1,15 @@ import createIcon from './createIcon' -export default createIcon(({ size, color }) => ( +const ANSIBLE_RED = '#EE0000' + +export default createIcon(({ size, color, fullColor }) => ( - + diff --git a/assets/design-system/src/components/icons/CSharpLogoIcon.tsx b/assets/design-system/src/components/icons/CSharpLogoIcon.tsx new file mode 100644 index 0000000000..0b7edcd878 --- /dev/null +++ b/assets/design-system/src/components/icons/CSharpLogoIcon.tsx @@ -0,0 +1,17 @@ +import createIcon from './createIcon' + +// C# from Simple Icons: https://simpleicons.org/icons/csharp +export default createIcon(({ size, color, fullColor }) => ( + + + +)) diff --git a/assets/design-system/src/components/icons/CppLogoIcon.tsx b/assets/design-system/src/components/icons/CppLogoIcon.tsx new file mode 100644 index 0000000000..84ce535bab --- /dev/null +++ b/assets/design-system/src/components/icons/CppLogoIcon.tsx @@ -0,0 +1,17 @@ +import createIcon from './createIcon' + +// C++ from Simple Icons: https://simpleicons.org/icons/cplusplus +export default createIcon(({ size, color, fullColor }) => ( + + + +)) diff --git a/assets/design-system/src/components/icons/ElixirLogoIcon.tsx b/assets/design-system/src/components/icons/ElixirLogoIcon.tsx new file mode 100644 index 0000000000..9f0d8a2e26 --- /dev/null +++ b/assets/design-system/src/components/icons/ElixirLogoIcon.tsx @@ -0,0 +1,17 @@ +import createIcon from './createIcon' + +// Elixir from Simple Icons: https://simpleicons.org/icons/elixir +export default createIcon(({ size, color, fullColor, mode }) => ( + + + +)) diff --git a/assets/design-system/src/components/icons/GitHubLogoIcon.tsx b/assets/design-system/src/components/icons/GitHubLogoIcon.tsx index 34b08ea3de..14ac99cc4d 100644 --- a/assets/design-system/src/components/icons/GitHubLogoIcon.tsx +++ b/assets/design-system/src/components/icons/GitHubLogoIcon.tsx @@ -1,6 +1,6 @@ import createIcon from './createIcon' -export default createIcon(({ size, color, fullColor }) => ( +export default createIcon(({ size, color, fullColor, mode }) => ( ( xmlns="http://www.w3.org/2000/svg" > diff --git a/assets/design-system/src/components/icons/GoLogoIcon.tsx b/assets/design-system/src/components/icons/GoLogoIcon.tsx new file mode 100644 index 0000000000..58c3cf74b3 --- /dev/null +++ b/assets/design-system/src/components/icons/GoLogoIcon.tsx @@ -0,0 +1,17 @@ +import createIcon from './createIcon' + +// Go wordmark from Simple Icons: https://simpleicons.org/icons/go +export default createIcon(({ size, color, fullColor }) => ( + + + +)) diff --git a/assets/design-system/src/components/icons/JavaLogoIcon.tsx b/assets/design-system/src/components/icons/JavaLogoIcon.tsx new file mode 100644 index 0000000000..74ae0d018b --- /dev/null +++ b/assets/design-system/src/components/icons/JavaLogoIcon.tsx @@ -0,0 +1,34 @@ +import createIcon from './createIcon' + +// Java coffee-cup mark from Devicon (Oracle does not publish a Simple Icons entry): +// https://github.com/devicons/devicon/blob/master/icons/java/java-original.svg +export default createIcon(({ size, color, fullColor }) => ( + + + + + + + +)) diff --git a/assets/design-system/src/components/icons/JavaScriptLogoIcon.tsx b/assets/design-system/src/components/icons/JavaScriptLogoIcon.tsx new file mode 100644 index 0000000000..3754d1a04d --- /dev/null +++ b/assets/design-system/src/components/icons/JavaScriptLogoIcon.tsx @@ -0,0 +1,17 @@ +import createIcon from './createIcon' + +// JavaScript from Simple Icons: https://simpleicons.org/icons/javascript +export default createIcon(({ size, color, fullColor }) => ( + + + +)) diff --git a/assets/design-system/src/components/icons/PhpLogoIcon.tsx b/assets/design-system/src/components/icons/PhpLogoIcon.tsx new file mode 100644 index 0000000000..d57c7a7c5d --- /dev/null +++ b/assets/design-system/src/components/icons/PhpLogoIcon.tsx @@ -0,0 +1,17 @@ +import createIcon from './createIcon' + +// PHP from Simple Icons: https://simpleicons.org/icons/php +export default createIcon(({ size, color, fullColor }) => ( + + + +)) diff --git a/assets/design-system/src/components/icons/PythonLogoIcon.tsx b/assets/design-system/src/components/icons/PythonLogoIcon.tsx new file mode 100644 index 0000000000..93c103cef0 --- /dev/null +++ b/assets/design-system/src/components/icons/PythonLogoIcon.tsx @@ -0,0 +1,17 @@ +import createIcon from './createIcon' + +// Python from Simple Icons: https://simpleicons.org/icons/python +export default createIcon(({ size, color, fullColor }) => ( + + + +)) diff --git a/assets/design-system/src/components/icons/RubyLogoIcon.tsx b/assets/design-system/src/components/icons/RubyLogoIcon.tsx new file mode 100644 index 0000000000..98d3da3ffd --- /dev/null +++ b/assets/design-system/src/components/icons/RubyLogoIcon.tsx @@ -0,0 +1,17 @@ +import createIcon from './createIcon' + +// Ruby from Simple Icons: https://simpleicons.org/icons/ruby +export default createIcon(({ size, color, fullColor }) => ( + + + +)) diff --git a/assets/design-system/src/components/icons/RustLogoIcon.tsx b/assets/design-system/src/components/icons/RustLogoIcon.tsx new file mode 100644 index 0000000000..deac819198 --- /dev/null +++ b/assets/design-system/src/components/icons/RustLogoIcon.tsx @@ -0,0 +1,17 @@ +import createIcon from './createIcon' + +// Rust from Simple Icons: https://simpleicons.org/icons/rust +export default createIcon(({ size, color, fullColor, mode }) => ( + + + +)) diff --git a/assets/design-system/src/components/icons/ScalaLogoIcon.tsx b/assets/design-system/src/components/icons/ScalaLogoIcon.tsx new file mode 100644 index 0000000000..f70606cf6a --- /dev/null +++ b/assets/design-system/src/components/icons/ScalaLogoIcon.tsx @@ -0,0 +1,17 @@ +import createIcon from './createIcon' + +// Scala from Simple Icons: https://simpleicons.org/icons/scala +export default createIcon(({ size, color, fullColor }) => ( + + + +)) diff --git a/assets/design-system/src/components/icons/TypeScriptLogoIcon.tsx b/assets/design-system/src/components/icons/TypeScriptLogoIcon.tsx new file mode 100644 index 0000000000..18ff626421 --- /dev/null +++ b/assets/design-system/src/components/icons/TypeScriptLogoIcon.tsx @@ -0,0 +1,17 @@ +import createIcon from './createIcon' + +// TypeScript from Simple Icons: https://simpleicons.org/icons/typescript +export default createIcon(({ size, color, fullColor }) => ( + + + +)) diff --git a/assets/design-system/src/components/icons/YamlLogoIcon.tsx b/assets/design-system/src/components/icons/YamlLogoIcon.tsx new file mode 100644 index 0000000000..8795bad24a --- /dev/null +++ b/assets/design-system/src/components/icons/YamlLogoIcon.tsx @@ -0,0 +1,17 @@ +import createIcon from './createIcon' + +// YAML wordmark from Simple Icons: https://simpleicons.org/icons/yaml +export default createIcon(({ size, color, fullColor }) => ( + + + +)) diff --git a/assets/design-system/src/components/table/Tr.tsx b/assets/design-system/src/components/table/Tr.tsx index a0774d34e1..830f96c318 100644 --- a/assets/design-system/src/components/table/Tr.tsx +++ b/assets/design-system/src/components/table/Tr.tsx @@ -26,13 +26,20 @@ export const Tr = styled.tr<{ display: 'contents', backgroundColor: theme.colors[ - tableCellColor(fillLevel, highlighted, raised, selectable, selected) + tableCellColor( + fillLevel, + highlighted, + raised, + selectable, + selected, + theme.mode + ) ], '&[data-expander-row]': { backgroundColor: theme.colors[ expandedBgColor ?? - tableCellHoverColor(fillLevel, selectable, selected) + tableCellHoverColor(fillLevel, selectable, selected, theme.mode) ], }, @@ -42,7 +49,9 @@ export const Tr = styled.tr<{ // highlight when hovered, but not when hovering nested actions '&:not(:has(button:hover, [data-clickable="true"]:hover)):hover': { backgroundColor: - theme.colors[tableCellHoverColor(fillLevel, selectable, selected)], + theme.colors[ + tableCellHoverColor(fillLevel, selectable, selected, theme.mode) + ], }, }), }) diff --git a/assets/design-system/src/components/table/colors.ts b/assets/design-system/src/components/table/colors.ts index d3c30e4213..e316456fdf 100644 --- a/assets/design-system/src/components/table/colors.ts +++ b/assets/design-system/src/components/table/colors.ts @@ -1,3 +1,4 @@ +import { type ColorMode } from '../../theme' import { type SemanticBorderKey } from '../../theme/borders' import { TableFillLevel } from './tableUtils' @@ -31,17 +32,33 @@ const tableFillLevelToCellBg = { 2: 'fill-two', } as const satisfies Record +/** Light: dedicated zebra tokens. Dark: legacy mapping (unchanged live behavior). */ const tableFillLevelToRaisedCellBg = { - 0: 'fill-zero-selected', - 1: 'fill-one-selected', - 2: 'fill-two-selected', -} as const satisfies Record + light: { + 0: 'fill-zero-raised', + 1: 'fill-one-raised', + 2: 'fill-two-raised', + }, + dark: { + 0: 'fill-zero-selected', + 1: 'fill-one-selected', + 2: 'fill-two-selected', + }, +} as const satisfies Record> +/** Light: true selection. Dark: legacy mapping used hover tokens for selected rows. */ const tableFillLevelToSelectedCellBg = { - 0: 'fill-zero-hover', - 1: 'fill-one-hover', - 2: 'fill-two-hover', -} as const satisfies Record + light: { + 0: 'fill-zero-selected', + 1: 'fill-one-selected', + 2: 'fill-two-selected', + }, + dark: { + 0: 'fill-zero-hover', + 1: 'fill-one-hover', + 2: 'fill-two-hover', + }, +} as const satisfies Record> export const tableFillLevelToHighlightedCellBg = { 0: 'fill-two', @@ -55,6 +72,12 @@ const tableFillLevelToHoverCellBg = { 2: 'fill-two-hover', } as const satisfies Record +const raisedCellBg = (fillLevel: TableFillLevel, mode: ColorMode) => + tableFillLevelToRaisedCellBg[mode][fillLevel] + +const selectedCellBg = (fillLevel: TableFillLevel, mode: ColorMode) => + tableFillLevelToSelectedCellBg[mode][fillLevel] + export const tableHeaderColor = ( fillLevel: TableFillLevel, highlighted: boolean @@ -68,23 +91,32 @@ export const tableCellColor = ( highlighted: boolean, raised: boolean, selectable: boolean, - selected: boolean + selected: boolean, + mode: ColorMode ) => highlighted ? tableFillLevelToHighlightedCellBg[fillLevel] : selected - ? tableFillLevelToSelectedCellBg[fillLevel] + ? selectedCellBg(fillLevel, mode) : raised || (selectable && !selected) - ? tableFillLevelToRaisedCellBg[fillLevel] + ? raisedCellBg(fillLevel, mode) : tableFillLevelToCellBg[fillLevel] export const tableCellHoverColor = ( fillLevel: TableFillLevel, selectable: boolean, - selected: boolean -) => - selectable + selected: boolean, + mode: ColorMode +) => { + if (mode === 'light') { + return selected + ? selectedCellBg(fillLevel, mode) + : tableFillLevelToHoverCellBg[fillLevel] + } + + return selectable ? selected - ? tableFillLevelToSelectedCellBg[fillLevel] - : tableFillLevelToRaisedCellBg[fillLevel] + ? selectedCellBg(fillLevel, mode) + : raisedCellBg(fillLevel, mode) : tableFillLevelToHoverCellBg[fillLevel] +} diff --git a/assets/design-system/src/hljs.ts b/assets/design-system/src/hljs.ts index 622297e3e5..d10d996fe9 100644 --- a/assets/design-system/src/hljs.ts +++ b/assets/design-system/src/hljs.ts @@ -236,9 +236,11 @@ hljs.registerLanguage('sql', sql) hljs.registerLanguage('stylus', stylus) hljs.registerLanguage('swift', swift) hljs.registerLanguage('terraform', terraform) +hljs.registerLanguage('hcl', terraform) hljs.registerLanguage('typescript', typescript) hljs.registerLanguage('vbnet', vbnet) hljs.registerLanguage('vbscript', vbscript) hljs.registerLanguage('vim', vim) hljs.registerLanguage('xml', xml) hljs.registerLanguage('yaml', yaml) +hljs.registerLanguage('yml', yaml) diff --git a/assets/design-system/src/icons.ts b/assets/design-system/src/icons.ts index 736da4f249..ab81effc19 100644 --- a/assets/design-system/src/icons.ts +++ b/assets/design-system/src/icons.ts @@ -77,9 +77,11 @@ export { default as ConsoleIcon } from './components/icons/ConsoleIcon' export { default as CookieIcon } from './components/icons/CookieIcon' export { default as CopyIcon } from './components/icons/CopyIcon' export { default as CostManagementIcon } from './components/icons/CostManagementIcon' +export { default as CppLogoIcon } from './components/icons/CppLogoIcon' export { default as CpuIcon } from './components/icons/CpuIcon' export { default as CraneIcon } from './components/icons/CraneIcon' export { default as CreditCardIcon } from './components/icons/CreditCardIcon' +export { default as CSharpLogoIcon } from './components/icons/CSharpLogoIcon' export { default as DashboardIcon } from './components/icons/DashboardIcon' export { default as DatabaseIcon } from './components/icons/DatabaseIcon' export { default as DatadogLogoIcon } from './components/icons/DatadogLogoIcon' @@ -102,6 +104,7 @@ export { default as EdgeComputeIcon } from './components/icons/EdgeComputeIcon' export { default as EditIcon } from './components/icons/EditIcon' export { default as EKSIcon } from './components/icons/EKSIcon' export { default as ElasticsearchLogoIcon } from './components/icons/ElasticsearchLogoIcon' +export { default as ElixirLogoIcon } from './components/icons/ElixirLogoIcon' export { default as EmojiHoverIcon } from './components/icons/EmojiHoverIcon' export { default as EmojiIcon } from './components/icons/EmojiIcon' export { default as ErrorIcon } from './components/icons/ErrorIcon' @@ -133,6 +136,7 @@ export { default as GitMergeIcon } from './components/icons/GitMergeIcon' export { default as GitPullIcon } from './components/icons/GitPullIcon' export { default as GKEIcon } from './components/icons/GKEIcon' export { default as GlobeIcon } from './components/icons/GlobeIcon' +export { default as GoLogoIcon } from './components/icons/GoLogoIcon' export { default as GoogleCloudLogoIcon } from './components/icons/GoogleCloudLogoIcon' export { default as GoogleCloudRunIcon } from './components/icons/GoogleCloudRunIcon' export { default as GoogleLogoIcon } from './components/icons/GoogleLogoIcon' @@ -157,6 +161,8 @@ export { default as InfrastructureIcon } from './components/icons/Infrastructure export { default as InstalledIcon } from './components/icons/InstalledIcon' export { default as InstallIcon } from './components/icons/InstallIcon' export { default as InvoicesIcon } from './components/icons/InvoicesIcon' +export { default as JavaLogoIcon } from './components/icons/JavaLogoIcon' +export { default as JavaScriptLogoIcon } from './components/icons/JavaScriptLogoIcon' export { default as JiraLogoIcon } from './components/icons/JiraLogoIcon' export { default as KeyIcon } from './components/icons/KeyIcon' export { default as KeyPairIcon } from './components/icons/KeyPairIcon' @@ -214,6 +220,7 @@ export { default as PeopleIcon } from './components/icons/PeopleIcon' export { default as PeoplePlusIcon } from './components/icons/PeoplePlusIcon' export { default as PersonIcon } from './components/icons/PersonIcon' export { default as PersonPlusIcon } from './components/icons/PersonPlusIcon' +export { default as PhpLogoIcon } from './components/icons/PhpLogoIcon' export { default as PipelineIcon } from './components/icons/PipelineIcon' export { default as PlanIcon } from './components/icons/PlanIcon' export { default as PlayIcon } from './components/icons/PlayIcon' @@ -233,6 +240,7 @@ export { default as ProtectedManagementClusterIcon } from './components/icons/Pr export { default as PrQueueIcon } from './components/icons/PrQueueIcon' export { default as PushPinFilledIcon } from './components/icons/PushPinFilledIcon' export { default as PushPinOutlineIcon } from './components/icons/PushPinOutlineIcon' +export { default as PythonLogoIcon } from './components/icons/PythonLogoIcon' export { default as QueuedOutlineIcon } from './components/icons/QueuedOutlineIcon' export { default as RamIcon } from './components/icons/RamIcon' export { default as ReloadIcon } from './components/icons/ReloadIcon' @@ -240,9 +248,12 @@ export { default as RestoreIcon } from './components/icons/RestoreIcon' export { default as ReturnIcon } from './components/icons/ReturnIcon' export { default as RobotIcon } from './components/icons/RobotIcon' export { default as RocketIcon } from './components/icons/RocketIcon' +export { default as RubyLogoIcon } from './components/icons/RubyLogoIcon' export { default as RunBookIcon } from './components/icons/RunBookIcon' +export { default as RustLogoIcon } from './components/icons/RustLogoIcon' export { default as ScrollIcon } from './components/icons/ScrollIcon' export { default as ScrollPlusIcon } from './components/icons/ScrollPlusIcon' +export { default as ScalaLogoIcon } from './components/icons/ScalaLogoIcon' export { default as SearchDocsIcon } from './components/icons/SearchDocsIcon' export { default as SearchIcon } from './components/icons/SearchIcon' export { default as SendMessageIcon } from './components/icons/SendMessageIcon' @@ -298,6 +309,7 @@ export { default as TrashCanIcon } from './components/icons/TrashCanIcon' export { default as TreeViewIcon } from './components/icons/TreeViewIcon' export { default as TwitterIcon } from './components/icons/TwitterIcon' export { default as TuningIcon } from './components/icons/TuningIcon' +export { default as TypeScriptLogoIcon } from './components/icons/TypeScriptLogoIcon' export { default as UbuntuLogoIcon } from './components/icons/UbuntuLogoIcon' export { default as UnknownIcon } from './components/icons/UnknownIcon' export { default as UpdatesIcon } from './components/icons/UpdatesIcon' @@ -316,4 +328,5 @@ export { default as WebhooksIcon } from './components/icons/WebhooksIcon' export { default as WindowsLogoIcon } from './components/icons/WindowsLogoIcon' export { default as WorkbenchIcon } from './components/icons/WorkbenchIcon' export { default as XAILogoIcon } from './components/icons/XAILogoIcon' +export { default as YamlLogoIcon } from './components/icons/YamlLogoIcon' export { default as YouTubeIcon } from './components/icons/YouTubeIcon' diff --git a/assets/design-system/src/markdoc/components/MdBaseNodes.tsx b/assets/design-system/src/markdoc/components/MdBaseNodes.tsx index 83c7bebaa7..40b0d9e83c 100644 --- a/assets/design-system/src/markdoc/components/MdBaseNodes.tsx +++ b/assets/design-system/src/markdoc/components/MdBaseNodes.tsx @@ -54,7 +54,10 @@ export const MdCode = styled.code.withConfig(commonCfg)(({ theme }) => ({ verticalAlign: 'baseline', padding: '0.1em 0.4em', margin: '-0.1em 0', - backgroundColor: theme.colors['fill-one'], + backgroundColor: + theme.mode === 'light' + ? theme.colors['fill-two'] + : theme.colors['fill-one'], borderRadius: theme.borderRadiuses.medium, })) export const MdHr = styled.hr.withConfig(commonCfg)(({ theme }) => ({ diff --git a/assets/design-system/src/theme.tsx b/assets/design-system/src/theme.tsx index 79911440a7..5f7229d64f 100644 --- a/assets/design-system/src/theme.tsx +++ b/assets/design-system/src/theme.tsx @@ -148,7 +148,7 @@ const getHonorableThemeProps = ({ mode }: { mode: ColorMode }) => { Root: [ { backgroundColor: 'action-primary', - borderRadius: 4, // TODO 3 or 6 + borderRadius: 3, fontWeight: 400, }, ], diff --git a/assets/design-system/src/theme/boxShadows.ts b/assets/design-system/src/theme/boxShadows.ts index faa7a67d9b..870ed34dc3 100644 --- a/assets/design-system/src/theme/boxShadows.ts +++ b/assets/design-system/src/theme/boxShadows.ts @@ -29,22 +29,22 @@ export const getBoxShadows = ({ mode }: { mode: ColorMode }) => } : { slight: [ - `0px 2px 10px 0px ${shadowL.alpha(0.04)}`, + `0px 1px 2px 0px ${shadowL.alpha(0.03)}`, `0px 1px 3px 0px ${shadowL.alpha(0.04)}`, ].join(','), moderate: [ - `0px 2px 10px 0px ${shadowL.alpha(0.08)}`, - `0px 2px 7px 1px ${shadowL.alpha(0.1)}`, + `0px 4px 12px 0px ${shadowL.alpha(0.06)}`, + `0px 1px 3px 0px ${shadowL.alpha(0.04)}`, ].join(','), - modal: `0px 10px 40px 0px ${shadowL.alpha(0.25)}`, + modal: `0px 8px 28px 0px ${shadowL.alpha(0.12)}`, slightPurple: [ - `0px 2px 10px 0px ${shadowLPurple.alpha(0.04)}`, + `0px 1px 2px 0px ${shadowLPurple.alpha(0.03)}`, `0px 1px 3px 0px ${shadowLPurple.alpha(0.04)}`, ].join(','), moderatePurple: [ - `0px 2px 10px 0px ${shadowLPurple.alpha(0.08)}`, - `0px 2px 7px 1px ${shadowLPurple.alpha(0.1)}`, + `0px 4px 12px 0px ${shadowLPurple.alpha(0.06)}`, + `0px 1px 3px 0px ${shadowLPurple.alpha(0.04)}`, ].join(','), - modalPurple: `0px 10px 40px 0px ${shadowLPurple.alpha(0.25)}`, + modalPurple: `0px 8px 28px 0px ${shadowLPurple.alpha(0.12)}`, }), }) as const satisfies Record diff --git a/assets/design-system/src/theme/colors-base.ts b/assets/design-system/src/theme/colors-base.ts index 9b4ec30313..919a7bb54b 100644 --- a/assets/design-system/src/theme/colors-base.ts +++ b/assets/design-system/src/theme/colors-base.ts @@ -26,6 +26,11 @@ export const grey = { 100: '#E3E4E7', 75: '#EBEDEE', 50: '#EEF0F1', + // Soft steps between page canvas (25) and 50 — Pure Light hover / raised ladder + 45: '#F0F2F3', + 40: '#F2F4F5', + 35: '#F5F6F6', + 30: '#F7F8F8', 25: '#F9FAFA', } as const satisfies Record diff --git a/assets/design-system/src/theme/colors-semantic-dark.ts b/assets/design-system/src/theme/colors-semantic-dark.ts index 5e5230c216..0bc74476a1 100644 --- a/assets/design-system/src/theme/colors-semantic-dark.ts +++ b/assets/design-system/src/theme/colors-semantic-dark.ts @@ -5,22 +5,30 @@ import { colorsCloudShellDark } from './colors-cloudshell-dark' import { colorsCodeBlockDark } from './colors-codeblock-dark' export const semanticColorsDark = { + // App shell canvas (html/body / console chrome) — independent from fill-zero + 'page-background': '#171a21', + // Fill // + // `*-raised` = zebra / striped rows (light-mode table stripes; dark mirrors legacy selected step). // fill-zero 'fill-zero': grey[925], + 'fill-zero-raised': grey[900], 'fill-zero-hover': grey[875], 'fill-zero-selected': grey[900], // fill-one 'fill-one': grey[875], + 'fill-one-raised': grey[850], 'fill-one-hover': grey[825], 'fill-one-selected': grey[850], // fill-two 'fill-two': grey[825], + 'fill-two-raised': grey[800], 'fill-two-hover': grey[775], 'fill-two-selected': grey[800], // fill-three 'fill-three': grey[775], + 'fill-three-raised': grey[750], 'fill-three-hover': grey[725], 'fill-three-selected': grey[750], // primary diff --git a/assets/design-system/src/theme/colors-semantic-light.ts b/assets/design-system/src/theme/colors-semantic-light.ts index 2af52f39d3..3e6de5db2d 100644 --- a/assets/design-system/src/theme/colors-semantic-light.ts +++ b/assets/design-system/src/theme/colors-semantic-light.ts @@ -6,28 +6,37 @@ import { colorsCodeBlockLight } from './colors-codeblock-light' import { semanticColorsDark } from './colors-semantic-dark' export const semanticColorsLight = { + // App shell canvas (html/body / console chrome) — independent from fill-zero + 'page-background': '#F9FAFA', + // Fill // + // Pure Light: white surfaces on a soft grey page. + // `*-raised` = zebra / striped rows — keep nearly white (matches Theme Lab Pure Light). // fill-zero 'fill-zero': '#FFFFFF', - 'fill-zero-hover': grey[75], - 'fill-zero-selected': '#FFFFFF', + 'fill-zero-raised': grey[25], + 'fill-zero-hover': grey[40], + 'fill-zero-selected': grey[45], // fill-one - 'fill-one': grey[50], - 'fill-one-hover': grey[75], - 'fill-one-selected': grey[50], + 'fill-one': '#FFFFFF', + 'fill-one-raised': grey[25], + 'fill-one-hover': grey[40], + 'fill-one-selected': grey[45], // fill-two - 'fill-two': grey[25], - 'fill-two-hover': grey[125], - 'fill-two-selected': grey[100], + 'fill-two': grey[30], + 'fill-two-raised': grey[30], + 'fill-two-hover': grey[45], + 'fill-two-selected': grey[50], // fill-three - 'fill-three': grey[100], - 'fill-three-hover': grey[150], - 'fill-three-selected': grey[175], + 'fill-three': grey[40], + 'fill-three-raised': grey[45], + 'fill-three-hover': grey[50], + 'fill-three-selected': grey[50], // primary 'fill-primary': purple[400], 'fill-primary-hover': purple[350], - // accent- used sparingly + // accent — nav / header chrome 'fill-accent': '#FFFFFF', // Action // @@ -55,13 +64,14 @@ export const semanticColorsLight = { // Border // - border: grey[100], - 'border-fill-one': grey[50], + // Soft hairlines — readable on white / soft grey without hard chrome + border: grey[75], + 'border-fill-one': grey[75], 'border-fill-two': grey[100], - 'border-fill-three': grey[150], + 'border-fill-three': grey[125], 'border-selected': grey[800], 'border-input': grey[100], - 'border-disabled': grey[75], + 'border-disabled': grey[50], 'border-primary': purple[500], 'border-secondary': blue[700], 'border-info': blue[600], diff --git a/assets/design-system/src/theme/lightElevatedSurface.ts b/assets/design-system/src/theme/lightElevatedSurface.ts new file mode 100644 index 0000000000..72334b3840 --- /dev/null +++ b/assets/design-system/src/theme/lightElevatedSurface.ts @@ -0,0 +1,41 @@ +import type { DefaultTheme } from 'styled-components' + +/** + * Light-mode elevated controls (inputs, selects, cards): soft hairline + slight + * lift. Shadows alone disappear on white-on-white (modals) and get clipped by + * overflow:hidden parents — keep a real border so the box always reads. + */ +export function lightElevatedSurface( + theme: DefaultTheme, + { + error = false, + disabled = false, + }: { + error?: boolean + disabled?: boolean + } = {} +): Record | null { + if (theme.mode !== 'light') return null + + if (error) { + return { + border: theme.borders.input, + borderColor: theme.colors['border-danger'], + boxShadow: 'none', + } + } + + if (disabled) { + return { + border: theme.borders.input, + borderColor: theme.colors['border-disabled'], + boxShadow: 'none', + } + } + + return { + border: theme.borders.input, + borderColor: theme.colors['border-input'], + boxShadow: theme.boxShadows.slight, + } +} diff --git a/assets/index.html b/assets/index.html index 2dc2a4b470..07c399ddd5 100644 --- a/assets/index.html +++ b/assets/index.html @@ -151,14 +151,16 @@ }