Migrate twenty ui to linaria (#18307)
## Migrate twenty-ui from Emotion to Linaria
Completes the migration of all `twenty-ui` components from Emotion
(runtime CSS-in-JS) to Linaria (zero-runtime, CSS extracted at build
time).
- Replaced `@emotion/styled` with `@linaria/react` across ~170 files
- Removed all Emotion dependencies from `twenty-ui`
- Introduced a CSS custom properties-based theme system:
`themeCssVariables` where every leaf is a `var(--t-xxx)` reference,
injected onto `document.documentElement` by
`ThemeCssVariableInjectorEffect`
- No more `theme` prop threading — styled components reference
`themeCssVariables.x.y` directly at build time
- Updated `twenty-front` consumers to remove `theme={theme}` prop
passing
**Before / After:**
```tsx
// Emotion
color: ${({ theme }) => theme.font.color.primary};
padding: ${({ theme }) => theme.spacing(4)};
// Linaria
color: ${themeCssVariables.font.color.primary};
padding: ${themeCssVariables.spacing[4]};
```
### Theme architecture
Two build-time utilities produce the theme system:
- **`buildThemeReferencingRootCssVariables`** — walks the theme object
and builds a nested mirror where every leaf is a `var(--t-xxx)` string
(evaluated at build time by wyw-in-js)
- **`prepareThemeForRootCssVariableInjection`** — walks the runtime
theme and collects flat `[--css-variable-name, value]` pairs, injected
onto `document.documentElement` by `ThemeCssVariableInjectorEffect`
Both share naming conventions (`camelToKebab`, `SPACING_VALUES`,
`formatSpacingKey`) and are unit tested.
### Spacing cleanup
Spacing scale now uses integers 0–32 (generated via loop), with `0.5`
and `1.5` as the only fractional exceptions. All other fractional
spacing usages (`0.25`, `0.75`, `1.25`, `2.5`, `3.5`) were replaced with
literal pixel values across ~20 twenty-front files.
### Framer Motion integration
Linaria doesn't support `styled(motion.div)` — wrapping a motion element
with `styled()` causes the component body to be stripped at build time.
Instead, we define the styled component first, then wrap it with
`motion.create()`:
```tsx
const StyledBarBase = styled.div`
background-color: ${themeCssVariables.font.color.primary};
height: 100%;
`;
const StyledBar = motion.create(StyledBarBase);
```
### Block interpolations
Linaria doesn't support interpolations that return multiple CSS
declarations (Linaria wraps the entire block in a single `var()`,
producing invalid CSS). These were split into individual property
interpolations:
```tsx
// Emotion — single interpolation returning multiple declarations
border-left: ${({ divider, theme }) => {
const border = `1px solid ${theme.border.color.light}`;
return divider ? `border-${divider}: ${border}` : '';
}}
// Linaria — one interpolation per property
border-left: ${({ divider }) =>
divider === 'left' ? `1px solid ${themeCssVariables.border.color.light}` : 'none'};
border-right: ${({ divider }) =>
divider === 'right' ? `1px solid ${themeCssVariables.border.color.light}` : 'none'};
```
### Dynamic styles via CSS variables
When a component needs to compute styles from multiple props with
complex branching logic (e.g. `Button` combining `variant`, `accent`,
`inverted`, `disabled`, `focus`, `position`), Linaria's prop
interpolations become unwieldy. In those cases we use a
`computeDynamicStyles` function that returns a `CSSProperties` object
injected via `style={}`, referenced from the static CSS with `var()`:
```tsx
const StyledButton = styled.button`
background: var(--btn-bg);
border-color: var(--btn-border-color);
&:hover { background: var(--btn-hover-bg); }
`;
const dynamicStyles = useMemo(() => {
const s = computeButtonDynamicStyles(variant, accent, ...);
return { '--btn-bg': s.background, '--btn-hover-bg': s.hoverBackground } as CSSProperties;
}, [variant, accent, ...]);
return <StyledButton style={dynamicStyles} />;
```
### CSS var + unit concatenation
CSS custom properties can't be concatenated with unit suffixes directly
(`var(--x)px` is invalid). Values that need units use `calc()`:
```tsx
// Broken
transition: background ${themeCssVariables.animation.duration.instant}s ease;
// Fixed
transition: background calc(${themeCssVariables.animation.duration.instant} * 1s) ease;
```
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
import { css, useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useIsMobile } from '@ui/utilities';
|
||||
import { getOsShortcutSeparator } from '@ui/utilities/device/getOsShortcutSeparator';
|
||||
import { type MotionProps, motion } from 'framer-motion';
|
||||
import React, { useContext, useMemo } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { Pill } from '@ui/components/Pill/Pill';
|
||||
import { ThemeContext, themeCssVariables } from '@ui/theme';
|
||||
import {
|
||||
type ButtonAccent,
|
||||
type ButtonPosition,
|
||||
type ButtonProps,
|
||||
type ButtonSize,
|
||||
type ButtonVariant,
|
||||
@@ -19,16 +21,294 @@ export type AnimatedButtonProps = ButtonProps &
|
||||
soonLabel?: string;
|
||||
};
|
||||
|
||||
type AnimatedButtonDynamicStyles = {
|
||||
background: string;
|
||||
borderColor: string;
|
||||
borderWidthOverride: string;
|
||||
boxShadow: string;
|
||||
color: string;
|
||||
hoverBackground: string;
|
||||
activeBackground: string;
|
||||
};
|
||||
|
||||
const computeAnimatedButtonDynamicStyles = (
|
||||
variant: ButtonVariant,
|
||||
inverted: boolean,
|
||||
accent: ButtonAccent,
|
||||
disabled: boolean,
|
||||
focus: boolean,
|
||||
position: ButtonPosition,
|
||||
): AnimatedButtonDynamicStyles => {
|
||||
const result: AnimatedButtonDynamicStyles = {
|
||||
background: 'transparent',
|
||||
borderColor: 'transparent',
|
||||
borderWidthOverride: '',
|
||||
boxShadow: 'none',
|
||||
color: themeCssVariables.font.color.secondary,
|
||||
hoverBackground: 'transparent',
|
||||
activeBackground: 'transparent',
|
||||
};
|
||||
|
||||
switch (variant) {
|
||||
case 'primary':
|
||||
switch (accent) {
|
||||
case 'default':
|
||||
result.background = !inverted
|
||||
? themeCssVariables.background.secondary
|
||||
: themeCssVariables.background.primary;
|
||||
result.borderColor = !inverted
|
||||
? !disabled && focus
|
||||
? themeCssVariables.color.blue
|
||||
: themeCssVariables.background.transparent.light
|
||||
: themeCssVariables.background.transparent.light;
|
||||
result.borderWidthOverride = '1px 1px 1px 1px';
|
||||
result.boxShadow =
|
||||
!disabled && focus
|
||||
? `0 0 0 3px ${
|
||||
!inverted
|
||||
? themeCssVariables.accent.tertiary
|
||||
: themeCssVariables.background.transparent.medium
|
||||
}`
|
||||
: 'none';
|
||||
result.color = !inverted
|
||||
? !disabled
|
||||
? themeCssVariables.font.color.secondary
|
||||
: themeCssVariables.font.color.extraLight
|
||||
: themeCssVariables.font.color.secondary;
|
||||
if (!disabled) {
|
||||
result.hoverBackground = !inverted
|
||||
? themeCssVariables.background.tertiary
|
||||
: themeCssVariables.background.secondary;
|
||||
result.activeBackground = !inverted
|
||||
? themeCssVariables.background.quaternary
|
||||
: themeCssVariables.background.tertiary;
|
||||
} else {
|
||||
result.hoverBackground = result.background;
|
||||
result.activeBackground = result.background;
|
||||
}
|
||||
break;
|
||||
case 'blue':
|
||||
result.background = !inverted
|
||||
? themeCssVariables.color.blue
|
||||
: themeCssVariables.background.primary;
|
||||
result.borderColor = !inverted
|
||||
? focus
|
||||
? themeCssVariables.color.blue
|
||||
: themeCssVariables.background.transparent.light
|
||||
: themeCssVariables.background.transparent.light;
|
||||
result.borderWidthOverride = '1px 1px 1px 1px';
|
||||
result.boxShadow =
|
||||
!disabled && focus
|
||||
? `0 0 0 3px ${
|
||||
!inverted
|
||||
? themeCssVariables.accent.tertiary
|
||||
: themeCssVariables.background.transparent.medium
|
||||
}`
|
||||
: 'none';
|
||||
result.color = !inverted
|
||||
? themeCssVariables.grayScale.gray1
|
||||
: themeCssVariables.color.blue;
|
||||
if (!disabled) {
|
||||
result.hoverBackground = !inverted
|
||||
? themeCssVariables.color.blue10
|
||||
: themeCssVariables.background.secondary;
|
||||
result.activeBackground = !inverted
|
||||
? themeCssVariables.color.blue12
|
||||
: themeCssVariables.background.tertiary;
|
||||
} else {
|
||||
result.hoverBackground = result.background;
|
||||
result.activeBackground = result.background;
|
||||
}
|
||||
break;
|
||||
case 'danger':
|
||||
result.background = !inverted
|
||||
? themeCssVariables.color.red
|
||||
: themeCssVariables.background.primary;
|
||||
result.borderColor = !inverted
|
||||
? focus
|
||||
? themeCssVariables.color.red
|
||||
: themeCssVariables.background.transparent.light
|
||||
: themeCssVariables.background.transparent.light;
|
||||
result.borderWidthOverride = '1px 1px';
|
||||
result.boxShadow =
|
||||
!disabled && focus
|
||||
? `0 0 0 3px ${
|
||||
!inverted
|
||||
? themeCssVariables.color.red3
|
||||
: themeCssVariables.background.transparent.medium
|
||||
}`
|
||||
: 'none';
|
||||
result.color = !inverted
|
||||
? themeCssVariables.background.primary
|
||||
: themeCssVariables.color.red;
|
||||
if (!disabled) {
|
||||
result.hoverBackground = !inverted
|
||||
? themeCssVariables.color.red8
|
||||
: themeCssVariables.background.secondary;
|
||||
result.activeBackground = !inverted
|
||||
? themeCssVariables.color.red10
|
||||
: themeCssVariables.background.tertiary;
|
||||
} else {
|
||||
result.hoverBackground = result.background;
|
||||
result.activeBackground = result.background;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'secondary':
|
||||
case 'tertiary':
|
||||
switch (accent) {
|
||||
case 'default':
|
||||
result.background = 'transparent';
|
||||
result.borderColor = !inverted
|
||||
? variant === 'secondary'
|
||||
? !disabled && focus
|
||||
? themeCssVariables.color.blue
|
||||
: themeCssVariables.background.transparent.medium
|
||||
: focus
|
||||
? themeCssVariables.color.blue
|
||||
: 'transparent'
|
||||
: variant === 'secondary'
|
||||
? focus || disabled
|
||||
? themeCssVariables.grayScale.gray1
|
||||
: themeCssVariables.background.transparent.primary
|
||||
: focus
|
||||
? themeCssVariables.grayScale.gray1
|
||||
: 'transparent';
|
||||
result.borderWidthOverride = '1px 1px 1px 1px';
|
||||
result.boxShadow =
|
||||
!disabled && focus
|
||||
? `0 0 0 3px ${
|
||||
!inverted
|
||||
? themeCssVariables.accent.tertiary
|
||||
: themeCssVariables.background.transparent.medium
|
||||
}`
|
||||
: 'none';
|
||||
result.color = !inverted
|
||||
? !disabled
|
||||
? themeCssVariables.font.color.secondary
|
||||
: themeCssVariables.font.color.extraLight
|
||||
: themeCssVariables.font.color.inverted;
|
||||
result.hoverBackground = !inverted
|
||||
? !disabled
|
||||
? themeCssVariables.background.transparent.light
|
||||
: 'transparent'
|
||||
: themeCssVariables.background.transparent.light;
|
||||
result.activeBackground = !inverted
|
||||
? !disabled
|
||||
? themeCssVariables.background.transparent.light
|
||||
: 'transparent'
|
||||
: themeCssVariables.background.transparent.medium;
|
||||
break;
|
||||
case 'blue':
|
||||
result.background = 'transparent';
|
||||
result.borderColor = !inverted
|
||||
? variant === 'secondary'
|
||||
? focus
|
||||
? themeCssVariables.color.blue
|
||||
: themeCssVariables.accent.primary
|
||||
: focus
|
||||
? themeCssVariables.color.blue
|
||||
: 'transparent'
|
||||
: variant === 'secondary'
|
||||
? focus || disabled
|
||||
? themeCssVariables.grayScale.gray1
|
||||
: themeCssVariables.background.transparent.primary
|
||||
: focus
|
||||
? themeCssVariables.grayScale.gray1
|
||||
: 'transparent';
|
||||
result.borderWidthOverride = '1px 1px 1px 1px';
|
||||
result.boxShadow =
|
||||
!disabled && focus
|
||||
? `0 0 0 3px ${
|
||||
!inverted
|
||||
? themeCssVariables.accent.tertiary
|
||||
: themeCssVariables.background.transparent.medium
|
||||
}`
|
||||
: 'none';
|
||||
result.color = !inverted
|
||||
? !disabled
|
||||
? themeCssVariables.color.blue
|
||||
: themeCssVariables.accent.accent4060
|
||||
: themeCssVariables.font.color.inverted;
|
||||
result.hoverBackground = !inverted
|
||||
? !disabled
|
||||
? themeCssVariables.accent.tertiary
|
||||
: 'transparent'
|
||||
: themeCssVariables.background.transparent.light;
|
||||
result.activeBackground = !inverted
|
||||
? !disabled
|
||||
? themeCssVariables.accent.secondary
|
||||
: 'transparent'
|
||||
: themeCssVariables.background.transparent.medium;
|
||||
break;
|
||||
case 'danger':
|
||||
result.background = 'transparent';
|
||||
result.borderColor = !inverted
|
||||
? variant === 'secondary'
|
||||
? focus
|
||||
? themeCssVariables.color.red
|
||||
: themeCssVariables.border.color.danger
|
||||
: focus
|
||||
? themeCssVariables.color.red
|
||||
: 'transparent'
|
||||
: variant === 'secondary'
|
||||
? focus || disabled
|
||||
? themeCssVariables.grayScale.gray1
|
||||
: themeCssVariables.background.transparent.primary
|
||||
: focus
|
||||
? themeCssVariables.grayScale.gray1
|
||||
: 'transparent';
|
||||
result.borderWidthOverride = '1px 1px 1px 1px';
|
||||
result.boxShadow =
|
||||
!disabled && focus
|
||||
? `0 0 0 3px ${
|
||||
!inverted
|
||||
? themeCssVariables.color.red3
|
||||
: themeCssVariables.background.transparent.medium
|
||||
}`
|
||||
: 'none';
|
||||
result.color = !inverted
|
||||
? themeCssVariables.font.color.danger
|
||||
: themeCssVariables.font.color.inverted;
|
||||
result.hoverBackground = !inverted
|
||||
? !disabled
|
||||
? themeCssVariables.background.danger
|
||||
: 'transparent'
|
||||
: themeCssVariables.background.transparent.light;
|
||||
result.activeBackground = !inverted
|
||||
? !disabled
|
||||
? themeCssVariables.background.danger
|
||||
: 'transparent'
|
||||
: themeCssVariables.background.transparent.medium;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (result.borderWidthOverride !== '' && position !== 'standalone') {
|
||||
switch (position) {
|
||||
case 'left':
|
||||
result.borderWidthOverride = '1px 0px 1px 1px';
|
||||
break;
|
||||
case 'middle':
|
||||
result.borderWidthOverride = '1px 0px 1px 0px';
|
||||
break;
|
||||
case 'right':
|
||||
result.borderWidthOverride = '1px 1px 1px 0px';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const StyledButton = styled.button<
|
||||
Pick<
|
||||
ButtonProps,
|
||||
| 'fullWidth'
|
||||
| 'variant'
|
||||
| 'inverted'
|
||||
| 'size'
|
||||
| 'position'
|
||||
| 'accent'
|
||||
| 'focus'
|
||||
| 'justify'
|
||||
| 'to'
|
||||
| 'target'
|
||||
@@ -37,292 +317,44 @@ const StyledButton = styled.button<
|
||||
>
|
||||
>`
|
||||
align-items: center;
|
||||
${({ theme, variant, inverted, accent, disabled, focus }) => {
|
||||
switch (variant) {
|
||||
case 'primary':
|
||||
switch (accent) {
|
||||
case 'default':
|
||||
return css`
|
||||
background: ${!inverted
|
||||
? theme.background.secondary
|
||||
: theme.background.primary};
|
||||
border-color: ${!inverted
|
||||
? !disabled && focus
|
||||
? theme.color.blue
|
||||
: theme.background.transparent.light
|
||||
: theme.background.transparent.light};
|
||||
border-width: 1px 1px 1px 1px !important;
|
||||
box-shadow: ${!disabled && focus
|
||||
? `0 0 0 3px ${
|
||||
!inverted
|
||||
? theme.accent.tertiary
|
||||
: theme.background.transparent.medium
|
||||
}`
|
||||
: 'none'};
|
||||
color: ${!inverted
|
||||
? !disabled
|
||||
? theme.font.color.secondary
|
||||
: theme.font.color.extraLight
|
||||
: theme.font.color.secondary};
|
||||
&:hover {
|
||||
background: ${!inverted
|
||||
? theme.background.tertiary
|
||||
: theme.background.secondary};
|
||||
}
|
||||
&:active {
|
||||
background: ${!inverted
|
||||
? theme.background.quaternary
|
||||
: theme.background.tertiary};
|
||||
}
|
||||
`;
|
||||
case 'blue':
|
||||
return css`
|
||||
background: ${!inverted
|
||||
? theme.color.blue
|
||||
: theme.background.primary};
|
||||
border-color: ${!inverted
|
||||
? focus
|
||||
? theme.color.blue
|
||||
: theme.background.transparent.light
|
||||
: theme.background.transparent.light};
|
||||
border-width: 1px 1px 1px 1px !important;
|
||||
box-shadow: ${!disabled && focus
|
||||
? `0 0 0 3px ${
|
||||
!inverted
|
||||
? theme.accent.tertiary
|
||||
: theme.background.transparent.medium
|
||||
}`
|
||||
: 'none'};
|
||||
color: ${!inverted ? theme.grayScale.gray1 : theme.color.blue};
|
||||
${disabled
|
||||
? ''
|
||||
: css`
|
||||
&:hover {
|
||||
background: ${!inverted
|
||||
? theme.color.blue10
|
||||
: theme.background.secondary};
|
||||
}
|
||||
&:active {
|
||||
background: ${!inverted
|
||||
? theme.color.blue12
|
||||
: theme.background.tertiary};
|
||||
}
|
||||
`}
|
||||
`;
|
||||
case 'danger':
|
||||
return css`
|
||||
background: ${!inverted
|
||||
? theme.color.red
|
||||
: theme.background.primary};
|
||||
border-color: ${!inverted
|
||||
? focus
|
||||
? theme.color.red
|
||||
: theme.background.transparent.light
|
||||
: theme.background.transparent.light};
|
||||
border-width: 1px 1px !important;
|
||||
box-shadow: ${!disabled && focus
|
||||
? `0 0 0 3px ${
|
||||
!inverted
|
||||
? theme.color.red3
|
||||
: theme.background.transparent.medium
|
||||
}`
|
||||
: 'none'};
|
||||
color: ${!inverted ? theme.background.primary : theme.color.red};
|
||||
${disabled
|
||||
? ''
|
||||
: css`
|
||||
&:hover {
|
||||
background: ${!inverted
|
||||
? theme.color.red8
|
||||
: theme.background.secondary};
|
||||
}
|
||||
&:active {
|
||||
background: ${!inverted
|
||||
? theme.color.red10
|
||||
: theme.background.tertiary};
|
||||
}
|
||||
`}
|
||||
`;
|
||||
}
|
||||
break;
|
||||
case 'secondary':
|
||||
case 'tertiary':
|
||||
switch (accent) {
|
||||
case 'default':
|
||||
return css`
|
||||
background: transparent;
|
||||
border-color: ${!inverted
|
||||
? variant === 'secondary'
|
||||
? !disabled && focus
|
||||
? theme.color.blue
|
||||
: theme.background.transparent.medium
|
||||
: focus
|
||||
? theme.color.blue
|
||||
: 'transparent'
|
||||
: variant === 'secondary'
|
||||
? focus || disabled
|
||||
? theme.grayScale.gray1
|
||||
: theme.background.transparent.primary
|
||||
: focus
|
||||
? theme.grayScale.gray1
|
||||
: 'transparent'};
|
||||
border-width: 1px 1px 1px 1px !important;
|
||||
box-shadow: ${!disabled && focus
|
||||
? `0 0 0 3px ${
|
||||
!inverted
|
||||
? theme.accent.tertiary
|
||||
: theme.background.transparent.medium
|
||||
}`
|
||||
: 'none'};
|
||||
color: ${!inverted
|
||||
? !disabled
|
||||
? theme.font.color.secondary
|
||||
: theme.font.color.extraLight
|
||||
: theme.font.color.inverted};
|
||||
&:hover {
|
||||
background: ${!inverted
|
||||
? !disabled
|
||||
? theme.background.transparent.light
|
||||
: 'transparent'
|
||||
: theme.background.transparent.light};
|
||||
}
|
||||
&:active {
|
||||
background: ${!inverted
|
||||
? !disabled
|
||||
? theme.background.transparent.light
|
||||
: 'transparent'
|
||||
: theme.background.transparent.medium};
|
||||
}
|
||||
`;
|
||||
case 'blue':
|
||||
return css`
|
||||
background: transparent;
|
||||
border-color: ${!inverted
|
||||
? variant === 'secondary'
|
||||
? focus
|
||||
? theme.color.blue
|
||||
: theme.accent.primary
|
||||
: focus
|
||||
? theme.color.blue
|
||||
: 'transparent'
|
||||
: variant === 'secondary'
|
||||
? focus || disabled
|
||||
? theme.grayScale.gray1
|
||||
: theme.background.transparent.primary
|
||||
: focus
|
||||
? theme.grayScale.gray1
|
||||
: 'transparent'};
|
||||
border-width: 1px 1px 1px 1px !important;
|
||||
box-shadow: ${!disabled && focus
|
||||
? `0 0 0 3px ${
|
||||
!inverted
|
||||
? theme.accent.tertiary
|
||||
: theme.background.transparent.medium
|
||||
}`
|
||||
: 'none'};
|
||||
color: ${!inverted
|
||||
? !disabled
|
||||
? theme.color.blue
|
||||
: theme.accent.accent4060
|
||||
: theme.font.color.inverted};
|
||||
&:hover {
|
||||
background: ${!inverted
|
||||
? !disabled
|
||||
? theme.accent.tertiary
|
||||
: 'transparent'
|
||||
: theme.background.transparent.light};
|
||||
}
|
||||
&:active {
|
||||
background: ${!inverted
|
||||
? !disabled
|
||||
? theme.accent.secondary
|
||||
: 'transparent'
|
||||
: theme.background.transparent.medium};
|
||||
}
|
||||
`;
|
||||
case 'danger':
|
||||
return css`
|
||||
background: transparent;
|
||||
border-color: ${!inverted
|
||||
? variant === 'secondary'
|
||||
? focus
|
||||
? theme.color.red
|
||||
: theme.border.color.danger
|
||||
: focus
|
||||
? theme.color.red
|
||||
: 'transparent'
|
||||
: variant === 'secondary'
|
||||
? focus || disabled
|
||||
? theme.grayScale.gray1
|
||||
: theme.background.transparent.primary
|
||||
: focus
|
||||
? theme.grayScale.gray1
|
||||
: 'transparent'};
|
||||
border-width: 1px 1px 1px 1px !important;
|
||||
box-shadow: ${!disabled && focus
|
||||
? `0 0 0 3px ${
|
||||
!inverted
|
||||
? theme.color.red3
|
||||
: theme.background.transparent.medium
|
||||
}`
|
||||
: 'none'};
|
||||
color: ${!inverted
|
||||
? theme.font.color.danger
|
||||
: theme.font.color.inverted};
|
||||
&:hover {
|
||||
background: ${!inverted
|
||||
? !disabled
|
||||
? theme.background.danger
|
||||
: 'transparent'
|
||||
: theme.background.transparent.light};
|
||||
}
|
||||
&:active {
|
||||
background: ${!inverted
|
||||
? !disabled
|
||||
? theme.background.danger
|
||||
: 'transparent'
|
||||
: theme.background.transparent.medium};
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
}}
|
||||
background: var(--abtn-bg);
|
||||
border-color: var(--abtn-border-color);
|
||||
border-width: var(--abtn-border-width);
|
||||
box-shadow: var(--abtn-box-shadow);
|
||||
color: var(--abtn-color);
|
||||
|
||||
&:hover {
|
||||
background: var(--abtn-hover-bg);
|
||||
}
|
||||
&:active {
|
||||
background: var(--abtn-active-bg);
|
||||
}
|
||||
|
||||
text-decoration: none;
|
||||
border-radius: ${({ position, theme }) => {
|
||||
border-radius: ${({ position }) => {
|
||||
switch (position) {
|
||||
case 'left':
|
||||
return `${theme.border.radius.sm} 0px 0px ${theme.border.radius.sm}`;
|
||||
return `${themeCssVariables.border.radius.sm} 0px 0px ${themeCssVariables.border.radius.sm}`;
|
||||
case 'right':
|
||||
return `0px ${theme.border.radius.sm} ${theme.border.radius.sm} 0px`;
|
||||
return `0px ${themeCssVariables.border.radius.sm} ${themeCssVariables.border.radius.sm} 0px`;
|
||||
case 'middle':
|
||||
return '0px';
|
||||
case 'standalone':
|
||||
return theme.border.radius.sm;
|
||||
return themeCssVariables.border.radius.sm;
|
||||
}
|
||||
return '';
|
||||
}};
|
||||
border-style: solid;
|
||||
border-width: ${({ variant, position }) => {
|
||||
switch (variant) {
|
||||
case 'primary':
|
||||
case 'secondary':
|
||||
return position === 'middle' ? '1px 0px' : '1px';
|
||||
case 'tertiary':
|
||||
return '0';
|
||||
}
|
||||
}};
|
||||
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-family: ${({ theme }) => theme.font.family};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-weight: 500;
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
gap: ${themeCssVariables.spacing[1]};
|
||||
height: ${({ size }) => (size === 'small' ? '24px' : '32px')};
|
||||
justify-content: ${({ justify }) => justify};
|
||||
padding: ${({ theme }) => {
|
||||
return `0 ${theme.spacing(2)}`;
|
||||
}};
|
||||
justify-content: ${({ justify }) => justify ?? ''};
|
||||
padding: 0 ${themeCssVariables.spacing[2]};
|
||||
|
||||
transition: background 0.1s ease;
|
||||
|
||||
@@ -343,18 +375,20 @@ const StyledSeparator = styled.div<{
|
||||
buttonSize: ButtonSize;
|
||||
accent: ButtonAccent;
|
||||
}>`
|
||||
background: ${({ theme, accent }) => {
|
||||
background: ${({ accent }) => {
|
||||
switch (accent) {
|
||||
case 'blue':
|
||||
return theme.border.color.blue;
|
||||
return themeCssVariables.border.color.blue;
|
||||
case 'danger':
|
||||
return theme.border.color.danger;
|
||||
return themeCssVariables.border.color.danger;
|
||||
default:
|
||||
return theme.font.color.light;
|
||||
return themeCssVariables.font.color.light;
|
||||
}
|
||||
}};
|
||||
height: ${({ theme, buttonSize }) =>
|
||||
theme.spacing(buttonSize === 'small' ? 2 : 4)};
|
||||
height: ${({ buttonSize }) =>
|
||||
buttonSize === 'small'
|
||||
? themeCssVariables.spacing[2]
|
||||
: themeCssVariables.spacing[4]};
|
||||
margin: 0;
|
||||
width: 1px;
|
||||
`;
|
||||
@@ -363,19 +397,19 @@ const StyledShortcutLabel = styled.div<{
|
||||
variant: ButtonVariant;
|
||||
accent: ButtonAccent;
|
||||
}>`
|
||||
color: ${({ theme, variant, accent }) => {
|
||||
color: ${({ variant, accent }) => {
|
||||
switch (accent) {
|
||||
case 'blue':
|
||||
return theme.border.color.blue;
|
||||
return themeCssVariables.border.color.blue;
|
||||
case 'danger':
|
||||
return variant === 'primary'
|
||||
? theme.border.color.danger
|
||||
: theme.color.red8;
|
||||
? themeCssVariables.border.color.danger
|
||||
: themeCssVariables.color.red8;
|
||||
default:
|
||||
return theme.font.color.light;
|
||||
return themeCssVariables.font.color.light;
|
||||
}
|
||||
}};
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
`;
|
||||
|
||||
const StyledIconContainer = styled(motion.div)`
|
||||
@@ -411,8 +445,29 @@ export const AnimatedButton = ({
|
||||
dataGloballyPreventClickOutside,
|
||||
soonLabel = 'Soon',
|
||||
}: AnimatedButtonProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const isMobile = useIsMobile();
|
||||
const isDisabled = soon || disabled;
|
||||
|
||||
const dynamicStyles = useMemo(() => {
|
||||
const s = computeAnimatedButtonDynamicStyles(
|
||||
variant,
|
||||
inverted,
|
||||
accent,
|
||||
isDisabled,
|
||||
focus,
|
||||
position,
|
||||
);
|
||||
return {
|
||||
'--abtn-bg': s.background,
|
||||
'--abtn-border-color': s.borderColor,
|
||||
'--abtn-border-width': s.borderWidthOverride || undefined,
|
||||
'--abtn-box-shadow': s.boxShadow,
|
||||
'--abtn-color': s.color,
|
||||
'--abtn-hover-bg': s.hoverBackground,
|
||||
'--abtn-active-bg': s.activeBackground,
|
||||
} as React.CSSProperties;
|
||||
}, [variant, inverted, accent, isDisabled, focus, position]);
|
||||
|
||||
const ButtonComponent = to ? Link : 'button';
|
||||
|
||||
@@ -420,15 +475,12 @@ export const AnimatedButton = ({
|
||||
<StyledButton
|
||||
as={ButtonComponent}
|
||||
fullWidth={fullWidth}
|
||||
variant={variant}
|
||||
inverted={inverted}
|
||||
size={size}
|
||||
position={position}
|
||||
disabled={soon || disabled}
|
||||
focus={focus}
|
||||
disabled={isDisabled}
|
||||
justify={justify}
|
||||
accent={accent}
|
||||
className={className}
|
||||
style={dynamicStyles}
|
||||
onClick={onClick}
|
||||
to={to}
|
||||
target={target}
|
||||
|
||||
Reference in New Issue
Block a user