feat(SW-375): new tokens
new asset generation logic BREAKING CHANGE: New tokens.
This commit is contained in:
151
packages/design-system/lib/components/Button/Button.stories.tsx
Normal file
151
packages/design-system/lib/components/Button/Button.stories.tsx
Normal file
@@ -0,0 +1,151 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import { fn } from '@storybook/test'
|
||||
|
||||
import { Button } from './Button'
|
||||
import { config as buttonConfig } from './variants'
|
||||
import { config as typographyConfig } from '../Typography/variants'
|
||||
|
||||
const meta: Meta<typeof Button> = {
|
||||
title: 'Components/Button',
|
||||
component: Button,
|
||||
argTypes: {
|
||||
onPress: {
|
||||
table: {
|
||||
disable: true,
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
control: 'select',
|
||||
options: Object.keys(typographyConfig.variants.variant),
|
||||
},
|
||||
variant: {
|
||||
control: 'select',
|
||||
options: Object.keys(buttonConfig.variants.variant),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
type Story = StoryObj<typeof Button>
|
||||
|
||||
export const PrimaryDefault: Story = {
|
||||
args: {
|
||||
onPress: fn(),
|
||||
children: 'Primary button',
|
||||
typography: 'Body/Paragraph/mdBold',
|
||||
variant: 'Primary',
|
||||
},
|
||||
}
|
||||
|
||||
export const PrimaryLarge: Story = {
|
||||
args: {
|
||||
...PrimaryDefault.args,
|
||||
size: 'Large',
|
||||
},
|
||||
}
|
||||
|
||||
export const PrimaryMedium: Story = {
|
||||
args: {
|
||||
...PrimaryDefault.args,
|
||||
size: 'Medium',
|
||||
},
|
||||
}
|
||||
|
||||
export const PrimarySmall: Story = {
|
||||
args: {
|
||||
...PrimaryDefault.args,
|
||||
size: 'Small',
|
||||
},
|
||||
}
|
||||
|
||||
export const SecondaryDefault: Story = {
|
||||
args: {
|
||||
onPress: fn(),
|
||||
children: 'Secondary button',
|
||||
typography: 'Body/Paragraph/mdBold',
|
||||
variant: 'Secondary',
|
||||
},
|
||||
}
|
||||
|
||||
export const SecondaryLarge: Story = {
|
||||
args: {
|
||||
...SecondaryDefault.args,
|
||||
size: 'Large',
|
||||
},
|
||||
}
|
||||
|
||||
export const SecondaryMedium: Story = {
|
||||
args: {
|
||||
...SecondaryDefault.args,
|
||||
size: 'Medium',
|
||||
},
|
||||
}
|
||||
|
||||
export const SecondarySmall: Story = {
|
||||
args: {
|
||||
...SecondaryDefault.args,
|
||||
size: 'Small',
|
||||
},
|
||||
}
|
||||
|
||||
export const TertiaryDefault: Story = {
|
||||
args: {
|
||||
onPress: fn(),
|
||||
children: 'Tertiary button',
|
||||
typography: 'Body/Paragraph/mdBold',
|
||||
variant: 'Tertiary',
|
||||
},
|
||||
}
|
||||
|
||||
export const TertiaryLarge: Story = {
|
||||
args: {
|
||||
...TertiaryDefault.args,
|
||||
size: 'Large',
|
||||
},
|
||||
}
|
||||
|
||||
export const TertiaryMedium: Story = {
|
||||
args: {
|
||||
...TertiaryDefault.args,
|
||||
size: 'Medium',
|
||||
},
|
||||
}
|
||||
|
||||
export const TertiarySmall: Story = {
|
||||
args: {
|
||||
...TertiaryDefault.args,
|
||||
size: 'Small',
|
||||
},
|
||||
}
|
||||
|
||||
export const TextDefault: Story = {
|
||||
args: {
|
||||
onPress: fn(),
|
||||
children: 'Text button',
|
||||
typography: 'Body/Paragraph/mdBold',
|
||||
variant: 'Text',
|
||||
},
|
||||
}
|
||||
|
||||
export const TextLarge: Story = {
|
||||
args: {
|
||||
...TextDefault.args,
|
||||
size: 'Large',
|
||||
},
|
||||
}
|
||||
|
||||
export const TextMedium: Story = {
|
||||
args: {
|
||||
...TextDefault.args,
|
||||
size: 'Medium',
|
||||
},
|
||||
}
|
||||
|
||||
export const TextSmall: Story = {
|
||||
args: {
|
||||
...TextDefault.args,
|
||||
size: 'Small',
|
||||
},
|
||||
}
|
||||
29
packages/design-system/lib/components/Button/Button.tsx
Normal file
29
packages/design-system/lib/components/Button/Button.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
'use client'
|
||||
|
||||
import { Button as ButtonRAC } from 'react-aria-components'
|
||||
|
||||
import { variants } from './variants'
|
||||
|
||||
import type { ButtonProps } from './types'
|
||||
|
||||
export function Button({
|
||||
variant,
|
||||
color,
|
||||
size,
|
||||
|
||||
typography,
|
||||
className,
|
||||
|
||||
...props
|
||||
}: ButtonProps) {
|
||||
const classNames = variants({
|
||||
variant,
|
||||
color,
|
||||
size,
|
||||
|
||||
typography,
|
||||
className,
|
||||
})
|
||||
|
||||
return <ButtonRAC {...props} className={classNames} />
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
.button {
|
||||
border-radius: var(--Corner-radius-rounded);
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.size-large {
|
||||
padding: var(--Space-x2) var(--Space-x3);
|
||||
}
|
||||
|
||||
.size-medium {
|
||||
padding: var(--Space-x15) var(--Space-x2);
|
||||
}
|
||||
|
||||
.size-small {
|
||||
padding: 10px var(--Space-x2);
|
||||
}
|
||||
|
||||
.variant-primary {
|
||||
background-color: var(--Component-Button-Brand-Primary-Default);
|
||||
border-color: var(--Component-Button-Brand-Primary-Border-Default);
|
||||
color: var(--Component-Button-Brand-Primary-On-fill-Default);
|
||||
}
|
||||
|
||||
.variant-primary:hover {
|
||||
background-color: var(--Component-Button-Brand-Primary-Hover);
|
||||
border-color: var(--Component-Button-Brand-Primary-Border-Hover);
|
||||
color: var(--Component-Button-Brand-Primary-On-fill-Hover);
|
||||
}
|
||||
|
||||
.variant-primary:disabled {
|
||||
background-color: var(--Component-Button-Brand-Primary-Disabled);
|
||||
border-color: var(--Component-Button-Brand-Primary-Border-Disabled);
|
||||
color: var(--Component-Button-Brand-Primary-On-fill-Disabled);
|
||||
}
|
||||
|
||||
.variant-secondary {
|
||||
background-color: var(--Component-Button-Brand-Secondary-Default);
|
||||
border-color: var(--Component-Button-Brand-Secondary-Border-Default);
|
||||
color: var(--Component-Button-Brand-Secondary-On-fill-Default);
|
||||
}
|
||||
|
||||
.variant-secondary:hover {
|
||||
background-color: var(--Component-Button-Brand-Secondary-Hover);
|
||||
border-color: var(--Component-Button-Brand-Secondary-Border-Hover);
|
||||
color: var(--Component-Button-Brand-Secondary-On-fill-Hover);
|
||||
}
|
||||
|
||||
.variant-secondary:disabled {
|
||||
background-color: var(--Component-Button-Brand-Secondary-Disabled);
|
||||
border-color: var(--Component-Button-Brand-Secondary-Border-Disabled);
|
||||
color: var(--Component-Button-Brand-Secondary-On-fill-Disabled);
|
||||
}
|
||||
|
||||
.variant-tertiary {
|
||||
background-color: var(--Component-Button-Brand-Tertiary-Default);
|
||||
border-color: var(--Component-Button-Brand-Tertiary-Border-Default);
|
||||
color: var(--Component-Button-Brand-Tertiary-On-fill-Default);
|
||||
}
|
||||
|
||||
.variant-tertiary:hover {
|
||||
background-color: var(--Component-Button-Brand-Tertiary-Hover);
|
||||
border-color: var(--Component-Button-Brand-Tertiary-Border-Hover);
|
||||
color: var(--Component-Button-Brand-Tertiary-On-fill-Hover);
|
||||
}
|
||||
|
||||
.variant-tertiary:disabled {
|
||||
background-color: var(--Component-Button-Brand-Tertiary-Disabled);
|
||||
border-color: var(--Component-Button-Brand-Tertiary-Border-Disabled);
|
||||
color: var(--Component-Button-Brand-Tertiary-On-fill-Disabled);
|
||||
}
|
||||
|
||||
.variant-text {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
/* TODO: Missing text variant tokens for button */
|
||||
color: var(--Scandic-Red-100);
|
||||
padding: var(--Space-x15) 0;
|
||||
}
|
||||
10
packages/design-system/lib/components/Button/types.ts
Normal file
10
packages/design-system/lib/components/Button/types.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Button } from 'react-aria-components'
|
||||
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
import type { ComponentProps } from 'react'
|
||||
|
||||
import type { variants } from './variants'
|
||||
|
||||
export interface ButtonProps
|
||||
extends ComponentProps<typeof Button>,
|
||||
VariantProps<typeof variants> {}
|
||||
32
packages/design-system/lib/components/Button/variants.ts
Normal file
32
packages/design-system/lib/components/Button/variants.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { cva } from 'class-variance-authority'
|
||||
|
||||
import { withTypography } from '../Typography/variants'
|
||||
|
||||
import styles from './button.module.css'
|
||||
|
||||
export const config = {
|
||||
variants: {
|
||||
variant: {
|
||||
Primary: styles['variant-primary'],
|
||||
Secondary: styles['variant-secondary'],
|
||||
Tertiary: styles['variant-tertiary'],
|
||||
Text: styles['variant-text'],
|
||||
},
|
||||
color: {
|
||||
Primary: styles['color-primary'],
|
||||
Inverted: styles['color-inverted'],
|
||||
},
|
||||
size: {
|
||||
Small: styles['size-small'],
|
||||
Medium: styles['size-medium'],
|
||||
Large: styles['size-large'],
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'Primary',
|
||||
color: 'Primary',
|
||||
size: 'Large',
|
||||
},
|
||||
} as const
|
||||
|
||||
export const variants = cva(styles.button, withTypography(config))
|
||||
@@ -1,27 +0,0 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
import { Card } from './Card.tsx'
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Layout/Card',
|
||||
component: Card,
|
||||
argTypes: {
|
||||
intent: {
|
||||
control: 'select',
|
||||
options: ['basic', 'border'],
|
||||
},
|
||||
},
|
||||
args: {
|
||||
intent: 'basic',
|
||||
},
|
||||
} satisfies Meta<typeof Card>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {}
|
||||
|
||||
export const WithBorder: Story = {
|
||||
args: {
|
||||
intent: 'border',
|
||||
},
|
||||
}
|
||||
25
packages/design-system/lib/components/Card/Card.stories.tsx
Normal file
25
packages/design-system/lib/components/Card/Card.stories.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import { Card } from './Card.tsx'
|
||||
|
||||
const meta: Meta<typeof Card> = {
|
||||
title: 'Components/Card',
|
||||
component: Card,
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
type Story = StoryObj<typeof Card>
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
as: 'Default',
|
||||
},
|
||||
}
|
||||
|
||||
export const Featured: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
as: 'Featured',
|
||||
},
|
||||
}
|
||||
@@ -1,26 +1,11 @@
|
||||
import styles from './card.module.css'
|
||||
import { variants } from './variants'
|
||||
|
||||
import { cva, type VariantProps } from 'class-variance-authority'
|
||||
import type { CardProps } from './types'
|
||||
|
||||
const card = cva(styles.card, {
|
||||
variants: {
|
||||
intent: {
|
||||
basic: styles.basic,
|
||||
border: styles.border,
|
||||
},
|
||||
},
|
||||
// compoundVariants: [
|
||||
// { intent: "primary", size: "medium", className: styles.primaryMedium },
|
||||
// ],
|
||||
defaultVariants: {
|
||||
intent: 'basic',
|
||||
},
|
||||
})
|
||||
|
||||
export interface CardProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
VariantProps<typeof card> {}
|
||||
|
||||
export function Card({ className, intent }: CardProps) {
|
||||
return <div className={card({ intent, className })}>Card</div>
|
||||
export function Card({ as, className, children }: CardProps) {
|
||||
const classNames = variants({
|
||||
as,
|
||||
className,
|
||||
})
|
||||
return <div className={classNames}>{children}</div>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
import { fn } from '@storybook/test'
|
||||
|
||||
import { themes } from '../../../../.storybook/preview'
|
||||
|
||||
import { Card } from '../'
|
||||
import { Typography } from '../../Typography'
|
||||
import { Button } from '../../Button'
|
||||
|
||||
type CompositionProps = React.ComponentPropsWithoutRef<typeof Card> & {
|
||||
_onPrimaryPress?: () => void
|
||||
_onSecondaryPress?: () => void
|
||||
inMainArea: boolean
|
||||
showTitle: boolean
|
||||
showPreamble: boolean
|
||||
showPrimaryButton: boolean
|
||||
showSecondaryButton: boolean
|
||||
}
|
||||
|
||||
const meta: Meta<CompositionProps> = {
|
||||
title: 'Compositions/Card',
|
||||
component: Card,
|
||||
decorators: [
|
||||
(Story, context) => {
|
||||
if (context.name.toLowerCase().indexOf('all themes') >= 0) {
|
||||
return (
|
||||
<>
|
||||
<h1>{context.name}</h1>
|
||||
{Object.entries(themes.themes).map(([key, value]) => {
|
||||
return (
|
||||
<div className={value} style={{ padding: '1em 0' }}>
|
||||
<h2 style={{ paddingBottom: '0.5em' }}>{key}</h2>
|
||||
<Story />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex' }}>
|
||||
<Story />
|
||||
</div>
|
||||
)
|
||||
},
|
||||
],
|
||||
argTypes: {
|
||||
inMainArea: {
|
||||
name: 'Is in main area',
|
||||
},
|
||||
showTitle: {
|
||||
name: 'Composition: Show title',
|
||||
},
|
||||
showPreamble: {
|
||||
name: 'Composition: Show preamble',
|
||||
},
|
||||
showPrimaryButton: {
|
||||
name: 'Composition: Show primary button',
|
||||
},
|
||||
showSecondaryButton: {
|
||||
name: 'Composition: Show secondary button',
|
||||
},
|
||||
_onPrimaryPress: {
|
||||
table: {
|
||||
disable: true,
|
||||
},
|
||||
},
|
||||
_onSecondaryPress: {
|
||||
table: {
|
||||
disable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
render: ({
|
||||
inMainArea,
|
||||
showTitle,
|
||||
showPreamble,
|
||||
showPrimaryButton,
|
||||
showSecondaryButton,
|
||||
...args
|
||||
}) => (
|
||||
<Card {...args}>
|
||||
{showTitle && (
|
||||
<Typography variant="Title/md">
|
||||
<h3>Content Card</h3>
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
{showPreamble && (
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>
|
||||
Mattis sit duis pulvinar ultricies auctor euismod. Augue mattis
|
||||
mauris at est iaculis pulvinar pulvinar.
|
||||
</p>
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
{showPrimaryButton && inMainArea && (
|
||||
<Button
|
||||
size={'Large'}
|
||||
variant="Primary"
|
||||
typography="Body/Paragraph/mdBold"
|
||||
onPress={args._onPrimaryPress}
|
||||
>
|
||||
Primary action
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{showPrimaryButton && !inMainArea && (
|
||||
<Button
|
||||
size={'Small'}
|
||||
variant="Tertiary"
|
||||
typography="Body/Paragraph/mdBold"
|
||||
onPress={args._onPrimaryPress}
|
||||
>
|
||||
Primary action
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{showSecondaryButton && (
|
||||
<Button
|
||||
size={inMainArea ? 'Large' : 'Small'}
|
||||
variant="Secondary"
|
||||
onPress={args._onSecondaryPress}
|
||||
typography="Body/Paragraph/mdBold"
|
||||
>
|
||||
Secondary action
|
||||
</Button>
|
||||
)}
|
||||
</Card>
|
||||
),
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
type Story = StoryObj<CompositionProps>
|
||||
|
||||
export const ContentCardMainArea: Story = {
|
||||
args: {
|
||||
as: 'Default',
|
||||
inMainArea: true,
|
||||
showTitle: true,
|
||||
showPreamble: true,
|
||||
showPrimaryButton: true,
|
||||
showSecondaryButton: true,
|
||||
_onPrimaryPress: fn(),
|
||||
_onSecondaryPress: fn(),
|
||||
},
|
||||
}
|
||||
|
||||
export const ContentCardNonMainArea: Story = {
|
||||
args: {
|
||||
as: 'Default',
|
||||
inMainArea: false,
|
||||
showTitle: true,
|
||||
showPreamble: true,
|
||||
showPrimaryButton: true,
|
||||
showSecondaryButton: true,
|
||||
_onPrimaryPress: fn(),
|
||||
_onSecondaryPress: fn(),
|
||||
},
|
||||
}
|
||||
|
||||
export const ContentCardMainAreaAllThemes: Story = {
|
||||
...ContentCardMainArea,
|
||||
}
|
||||
|
||||
export const ContentCardNonMainAreaAllThemes: Story = {
|
||||
...ContentCardNonMainArea,
|
||||
}
|
||||
@@ -1,13 +1,22 @@
|
||||
.card {
|
||||
background: #fff;
|
||||
padding: var(--Space-x2) var(--Space-x3);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
align-self: stretch;
|
||||
gap: var(--Space-x15);
|
||||
}
|
||||
|
||||
.basic {
|
||||
outline: solid 1px green;
|
||||
.Default {
|
||||
border: solid 1px var(--Border-Default);
|
||||
background-color: var(--Surface-Secondary-Default);
|
||||
border-radius: var(--Corner-radius-md);
|
||||
color: var(--Text-Default);
|
||||
}
|
||||
|
||||
.border {
|
||||
border-style: solid;
|
||||
border-width: 3px;
|
||||
border-color: var(--color-brand-main-normal);
|
||||
.Featured {
|
||||
border: solid 1px var(--Border-Default);
|
||||
background-color: var(--Surface-Primary-Default);
|
||||
border-radius: var(--Corner-radius-md);
|
||||
color: var(--Text-Default);
|
||||
}
|
||||
|
||||
11
packages/design-system/lib/components/Card/types.ts
Normal file
11
packages/design-system/lib/components/Card/types.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
|
||||
import type { variants } from './variants'
|
||||
|
||||
type CardStyles = 'Default' | 'Featured'
|
||||
|
||||
export interface CardProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
VariantProps<typeof variants> {
|
||||
as: CardStyles
|
||||
}
|
||||
17
packages/design-system/lib/components/Card/variants.ts
Normal file
17
packages/design-system/lib/components/Card/variants.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { cva } from 'class-variance-authority'
|
||||
|
||||
import styles from './card.module.css'
|
||||
|
||||
const config = {
|
||||
variants: {
|
||||
as: {
|
||||
Default: styles.Default,
|
||||
Featured: styles.Featured,
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
as: 'Default',
|
||||
},
|
||||
} as const
|
||||
|
||||
export const variants = cva(styles.card, config)
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import { fn } from '@storybook/test'
|
||||
|
||||
import { ChipButton } from './ChipButton.tsx'
|
||||
import ChevronRightSmallIcon from '../Icons/ChevronRightSmall.tsx'
|
||||
|
||||
const meta: Meta<typeof ChipButton> = {
|
||||
title: 'Components/Chip/ChipButton 🚧',
|
||||
component: ChipButton,
|
||||
argTypes: {
|
||||
onPress: {
|
||||
table: {
|
||||
disable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
type Story = StoryObj<typeof ChipButton>
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
onPress: fn(),
|
||||
children: (
|
||||
<>
|
||||
Button Chip <ChevronRightSmallIcon width={20} height={20} />
|
||||
</>
|
||||
),
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Button as ButtonRAC } from 'react-aria-components'
|
||||
|
||||
import { Typography } from '../Typography'
|
||||
|
||||
import styles from './chip-button.module.css'
|
||||
|
||||
import type { ComponentPropsWithRef } from 'react'
|
||||
|
||||
export function ChipButton({
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: ComponentPropsWithRef<typeof ButtonRAC>) {
|
||||
return (
|
||||
<Typography variant="Body/Supporting text (caption)/smBold">
|
||||
<ButtonRAC {...props} className={[styles.chip, className].join(' ')}>
|
||||
{children}
|
||||
</ButtonRAC>
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
.chip {
|
||||
background-color: var(--Component-Button-Inverted-Default);
|
||||
border-color: var(--Component-Button-Inverted-Border-Default);
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-radius: var(--Corner-radius-sm);
|
||||
padding: var(--Space-x1) var(--Space-x15);
|
||||
color: var(--Text-Interactive-Default);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.chip:hover {
|
||||
/* TODO: change to proper Component-variable once it is available */
|
||||
background-color: var(--Scandic-Peach-10);
|
||||
/* TODO: change to proper Component-variable once it is available */
|
||||
color: var(--Scandic-Red-100);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { ChipButton } from './ChipButton'
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import { ChipLink } from './ChipLink.tsx'
|
||||
import ChevronRightSmallIcon from '../Icons/ChevronRightSmall.tsx'
|
||||
|
||||
const meta: Meta<typeof ChipLink> = {
|
||||
title: 'Components/Chip/ChipLInk 🚧',
|
||||
component: ChipLink,
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
type Story = StoryObj<typeof ChipLink>
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
href: '/',
|
||||
onClick: (e) => e.preventDefault(),
|
||||
children: (
|
||||
<>
|
||||
Link Chip <ChevronRightSmallIcon width={20} height={20} />
|
||||
</>
|
||||
),
|
||||
},
|
||||
}
|
||||
19
packages/design-system/lib/components/ChipLink/ChipLink.tsx
Normal file
19
packages/design-system/lib/components/ChipLink/ChipLink.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Typography } from '../Typography'
|
||||
|
||||
import styles from './chip-link.module.css'
|
||||
|
||||
import type { PropsWithChildren } from 'react'
|
||||
|
||||
export function ChipLink({
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: PropsWithChildren<React.AnchorHTMLAttributes<HTMLAnchorElement>>) {
|
||||
return (
|
||||
<Typography variant="Body/Supporting text (caption)/smBold">
|
||||
<a {...props} className={[styles.chip, className].join(' ')}>
|
||||
{children}
|
||||
</a>
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
.chip {
|
||||
background-color: var(--Component-Button-Inverted-Default);
|
||||
border-color: var(--Component-Button-Inverted-Border-Default);
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-radius: var(--Corner-radius-sm);
|
||||
padding: var(--Space-x1) var(--Space-x15);
|
||||
color: var(--Text-Interactive-Default);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.chip:hover {
|
||||
/* TODO: change to proper Component-variable once it is available */
|
||||
background-color: var(--Scandic-Peach-10);
|
||||
/* TODO: change to proper Component-variable once it is available */
|
||||
color: var(--Scandic-Red-100);
|
||||
}
|
||||
1
packages/design-system/lib/components/ChipLink/index.tsx
Normal file
1
packages/design-system/lib/components/ChipLink/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { ChipLink } from './ChipLink'
|
||||
@@ -0,0 +1,61 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
import { fn } from '@storybook/test'
|
||||
|
||||
import { Chips } from './Chips.tsx'
|
||||
import { ChipLink } from '../ChipLink/ChipLink.tsx'
|
||||
import { ChipButton } from '../ChipButton/ChipButton.tsx'
|
||||
|
||||
import { Default as ChipLinkDefault } from '../ChipLink/ChipLink.stories.tsx'
|
||||
import { Default as ChipButtonDefault } from '../ChipButton/ChipButton.stories.tsx'
|
||||
|
||||
type StoryProps = React.ComponentPropsWithoutRef<typeof Chips> & {
|
||||
onPress: () => void
|
||||
}
|
||||
|
||||
const meta: Meta<StoryProps> = {
|
||||
title: 'Compositions/Chips',
|
||||
component: Chips,
|
||||
argTypes: {
|
||||
onPress: {
|
||||
table: {
|
||||
disable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
type Story = StoryObj<StoryProps>
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
onPress: fn(),
|
||||
},
|
||||
render: (args) => {
|
||||
return (
|
||||
<Chips>
|
||||
<ChipButton {...ChipButtonDefault.args} {...args} />
|
||||
<ChipButton {...ChipButtonDefault.args} {...args} />
|
||||
<ChipButton {...ChipButtonDefault.args} {...args} />
|
||||
<ChipLink {...ChipLinkDefault.args} {...args} />
|
||||
<ChipLink {...ChipLinkDefault.args} {...args} />
|
||||
<ChipLink {...ChipLinkDefault.args} {...args} />
|
||||
<ChipButton {...ChipButtonDefault.args} {...args} />
|
||||
<ChipButton {...ChipButtonDefault.args} {...args} />
|
||||
<ChipButton {...ChipButtonDefault.args} {...args} />
|
||||
<ChipLink {...ChipLinkDefault.args} {...args} />
|
||||
<ChipLink {...ChipLinkDefault.args} {...args} />
|
||||
<ChipLink {...ChipLinkDefault.args} {...args} />
|
||||
<ChipButton {...ChipButtonDefault.args} {...args} />
|
||||
<ChipButton {...ChipButtonDefault.args} {...args} />
|
||||
<ChipLink {...ChipLinkDefault.args} {...args} />
|
||||
<ChipButton {...ChipButtonDefault.args} {...args} />
|
||||
<ChipLink {...ChipLinkDefault.args} {...args} />
|
||||
<ChipLink {...ChipLinkDefault.args} {...args} />
|
||||
<ChipLink {...ChipLinkDefault.args} {...args} />
|
||||
<ChipButton {...ChipButtonDefault.args} {...args} />
|
||||
</Chips>
|
||||
)
|
||||
},
|
||||
}
|
||||
7
packages/design-system/lib/components/Chips/Chips.tsx
Normal file
7
packages/design-system/lib/components/Chips/Chips.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import styles from './chips.module.css'
|
||||
|
||||
import type { PropsWithChildren } from 'react'
|
||||
|
||||
export function Chips({ children }: PropsWithChildren) {
|
||||
return <div className={styles.chips}>{children}</div>
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
.chips {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--Space-x1);
|
||||
}
|
||||
1
packages/design-system/lib/components/Chips/index.tsx
Normal file
1
packages/design-system/lib/components/Chips/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { Chips } from './Chips'
|
||||
@@ -1,82 +0,0 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import { Button } from './Button'
|
||||
import { TestIcon } from '../Icon/TestIcon'
|
||||
|
||||
import '../../../style-current.css'
|
||||
|
||||
const meta = {
|
||||
title: 'Current web/Controls/Button',
|
||||
component: Button,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div>
|
||||
<Story />
|
||||
</div>
|
||||
),
|
||||
],
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
},
|
||||
} satisfies Meta<typeof Button>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Primary: Story = {
|
||||
args: {
|
||||
children: 'Primary Button',
|
||||
},
|
||||
}
|
||||
|
||||
export const Secondary: Story = {
|
||||
args: {
|
||||
children: 'Secondary Button',
|
||||
intent: 'secondary',
|
||||
},
|
||||
}
|
||||
|
||||
export const Large: Story = {
|
||||
args: {
|
||||
children: 'Large Button',
|
||||
size: 'large',
|
||||
},
|
||||
}
|
||||
|
||||
export const Small: Story = {
|
||||
args: {
|
||||
children: 'Small Button',
|
||||
size: 'small',
|
||||
},
|
||||
}
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
children: 'Disabled Button',
|
||||
isDisabled: true,
|
||||
},
|
||||
}
|
||||
|
||||
export const LeadingIcon: Story = {
|
||||
args: {
|
||||
children: [<TestIcon color="var(--Base-Text-Inverted)" />, 'Button'],
|
||||
},
|
||||
}
|
||||
|
||||
export const TrailingIcon: Story = {
|
||||
args: {
|
||||
children: ['Button', <TestIcon color="var(--Base-Text-Inverted)" />],
|
||||
},
|
||||
}
|
||||
|
||||
export const WithIcons: Story = {
|
||||
args: {
|
||||
children: [
|
||||
<TestIcon color="var(--Base-Text-Inverted)" />,
|
||||
<TestIcon color="var(--Base-Text-Inverted)" />,
|
||||
'Button',
|
||||
<TestIcon color="var(--Base-Text-Inverted)" />,
|
||||
<TestIcon color="var(--Base-Text-Inverted)" />,
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import styles from './button.module.css'
|
||||
|
||||
import { Button as ButtonComponent } from 'react-aria-components'
|
||||
import { cva } from 'class-variance-authority'
|
||||
|
||||
import type { ButtonProps as ButtonComponentProps } from 'react-aria-components'
|
||||
import type { ComponentProps } from '../../../types'
|
||||
|
||||
const config = {
|
||||
variants: {
|
||||
intent: {
|
||||
primary: styles.primary,
|
||||
secondary: styles.secondary,
|
||||
},
|
||||
size: {
|
||||
small: styles.small,
|
||||
normal: styles.normal,
|
||||
large: styles.large,
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
intent: 'primary',
|
||||
size: 'normal',
|
||||
},
|
||||
} as const
|
||||
|
||||
const button = cva(styles.button, config)
|
||||
|
||||
export type ButtonProps = ComponentProps<
|
||||
ButtonComponentProps,
|
||||
typeof button,
|
||||
never,
|
||||
'intent' | 'size'
|
||||
>
|
||||
|
||||
export function Button({
|
||||
children,
|
||||
className,
|
||||
intent = config.defaultVariants.intent,
|
||||
size = config.defaultVariants.size,
|
||||
...props
|
||||
}: ButtonProps) {
|
||||
return (
|
||||
<ButtonComponent className={button({ className, intent, size })} {...props}>
|
||||
{children}
|
||||
</ButtonComponent>
|
||||
)
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
.button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 8px 24px;
|
||||
background: none;
|
||||
color: var(--Base-Text-Inverted);
|
||||
border: none;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
transition: background-color 300ms ease;
|
||||
font-family: Helvetica;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* Primary styles */
|
||||
.primary {
|
||||
background-color: var(--Primary-Fill-Strong-Default);
|
||||
border: 2px solid transparent;
|
||||
outline: 1px solid transparent;
|
||||
}
|
||||
|
||||
.primary:hover {
|
||||
background: var(--Primary-Fill-Strong-Hovered);
|
||||
}
|
||||
|
||||
.primary:active,
|
||||
.primary:focus {
|
||||
border: 2px solid var(--Base-Text-Inverted);
|
||||
outline: 1px solid var(--Primary-Border-Strong);
|
||||
}
|
||||
|
||||
/* Secondary styles */
|
||||
.secondary {
|
||||
border: 1px solid var(--Base-Border-Normal);
|
||||
background-color: var(--Base-Background-Normal);
|
||||
color: var(--Primary-Text-Strong);
|
||||
}
|
||||
|
||||
.secondary:hover {
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
outline: 2px solid var(--Primary-Border-Hovered);
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
.secondary:active,
|
||||
.secondary:focus {
|
||||
border: 1px solid var(--Primary-Border-Strong);
|
||||
outline: 1px solid var(--Base-Border-Normal);
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
/* Disabled styles */
|
||||
.button:disabled {
|
||||
background-color: var(--Base-Fill-Disabled);
|
||||
color: var(--Base-Text-Disabled);
|
||||
cursor: not-allowed;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Sizes */
|
||||
.large {
|
||||
font-size: var(--typography-body-regular-font-size);
|
||||
line-height: var(--typography-body-regular-line-height);
|
||||
letter-spacing: var(--typography-body-regular-letter-spacing);
|
||||
}
|
||||
|
||||
.normal {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
letter-spacing: 0.096px;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
letter-spacing: 0.096px;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
export function TestIcon({ color = '#291710' }: { color?: string }) {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<mask
|
||||
id="mask0_69_3287"
|
||||
style={{ maskType: 'alpha' }}
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
>
|
||||
<rect width="24" height="24" fill="#D9D9D9" />
|
||||
</mask>
|
||||
<g mask="url(#mask0_69_3287)">
|
||||
<path
|
||||
d="M11.075 12.95V15.9375C11.075 16.1958 11.1667 16.4167 11.35 16.6C11.5333 16.7833 11.7542 16.875 12.0125 16.875C12.2708 16.875 12.4917 16.7833 12.675 16.6C12.8583 16.4167 12.95 16.1958 12.95 15.9375V12.95H15.9375C16.1958 12.95 16.4167 12.8583 16.6 12.675C16.7833 12.4917 16.875 12.2708 16.875 12.0125C16.875 11.7542 16.7833 11.5333 16.6 11.35C16.4167 11.1667 16.1958 11.075 15.9375 11.075H12.95V8.0625C12.95 7.80417 12.8583 7.58333 12.675 7.4C12.4917 7.21667 12.2708 7.125 12.0125 7.125C11.7542 7.125 11.5333 7.21667 11.35 7.4C11.1667 7.58333 11.075 7.80417 11.075 8.0625V11.075H8.0625C7.80417 11.075 7.58333 11.1667 7.4 11.35C7.21667 11.5333 7.125 11.7542 7.125 12.0125C7.125 12.2708 7.21667 12.4917 7.4 12.675C7.58333 12.8583 7.80417 12.95 8.0625 12.95H11.075ZM12 21.75C10.6516 21.75 9.38434 21.4936 8.19838 20.9809C7.01239 20.4682 5.98075 19.7724 5.10345 18.8934C4.22615 18.0145 3.53125 16.9826 3.01875 15.7978C2.50625 14.613 2.25 13.3471 2.25 12C2.25 10.6516 2.50636 9.38434 3.01908 8.19838C3.53179 7.01239 4.22762 5.98075 5.10658 5.10345C5.98553 4.22615 7.01739 3.53125 8.20218 3.01875C9.38698 2.50625 10.6529 2.25 12 2.25C13.3484 2.25 14.6157 2.50636 15.8016 3.01908C16.9876 3.53179 18.0193 4.22762 18.8966 5.10658C19.7739 5.98553 20.4688 7.01739 20.9813 8.20217C21.4938 9.38697 21.75 10.6529 21.75 12C21.75 13.3484 21.4936 14.6157 20.9809 15.8016C20.4682 16.9876 19.7724 18.0193 18.8934 18.8966C18.0145 19.7739 16.9826 20.4688 15.7978 20.9813C14.613 21.4938 13.3471 21.75 12 21.75ZM12 19.875C14.1917 19.875 16.0521 19.1104 17.5813 17.5813C19.1104 16.0521 19.875 14.1917 19.875 12C19.875 9.80833 19.1104 7.94792 17.5813 6.41875C16.0521 4.88958 14.1917 4.125 12 4.125C9.80833 4.125 7.94792 4.88958 6.41875 6.41875C4.88958 7.94792 4.125 9.80833 4.125 12C4.125 14.1917 4.88958 16.0521 6.41875 17.5813C7.94792 19.1104 9.80833 19.875 12 19.875Z"
|
||||
fill={color}
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export { TestIcon } from './TestIcon'
|
||||
@@ -0,0 +1,16 @@
|
||||
export default function ChevronRightSmallIcon(
|
||||
props: React.SVGAttributes<HTMLOrSVGElement>
|
||||
) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<path d="M12.65 12L8.77495 8.12497C8.59995 7.94997 8.51245 7.73538 8.51245 7.48122C8.51245 7.22705 8.59995 7.0083 8.77495 6.82497C8.94995 6.64163 9.16662 6.54788 9.42495 6.54372C9.68328 6.53955 9.90412 6.62913 10.0875 6.81247L14.6125 11.3375C14.7041 11.4291 14.7729 11.5312 14.8187 11.6437C14.8645 11.7562 14.8875 11.875 14.8875 12C14.8875 12.125 14.8645 12.2437 14.8187 12.3562C14.7729 12.4687 14.7041 12.5708 14.6125 12.6625L10.0875 17.1875C9.90412 17.3708 9.68328 17.4604 9.42495 17.4562C9.16662 17.4521 8.94995 17.3583 8.77495 17.175C8.59995 16.9916 8.51245 16.7729 8.51245 16.5187C8.51245 16.2646 8.59995 16.05 8.77495 15.875L12.65 12Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import {
|
||||
Meta,
|
||||
Title,
|
||||
Subtitle,
|
||||
Description,
|
||||
Controls,
|
||||
Stories,
|
||||
} from '@storybook/blocks'
|
||||
|
||||
import * as TypographyStories from './Typography.stories.tsx'
|
||||
|
||||
<Meta of={TypographyStories} />
|
||||
|
||||
<Title />
|
||||
|
||||
The source of the design system lives in Figma. Here, source means design tokens _and_ Figma styles.
|
||||
|
||||
Figma styles are a collection of design tokens that together define a style. For the most part it is used for typography. Design tokens define things like font family, font weight, font size, etc. On their own they don't do much as their meaning is lost without context of each other. Together though they define the typography to render.
|
||||
|
||||
To help communication between designers and developers it helps to have the same nomenclature. To achieve this goal the technical implementation of the design system in this repository uses two approaches; a `Typography` component and a higher order function.
|
||||
|
||||
<Subtitle>The component</Subtitle>
|
||||
|
||||
> The approach described in this section is platform agnostic. However this documentation is for the implementation aimed at web. The same can be achived for other platforms but currently it is only implemented for web.
|
||||
|
||||
The `Typography` component implements all the Figma styles for typography with [Class Variance Authority](https://cva.style/). This allows the consumers to use this component to apply typography styling to any component that accepts a class (the `className` prop) by wrapping them. The implementation is also supported by code completion furthering promoting the same nomenclature.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
import { Typography } from '@scandic-hotels/design-system'
|
||||
|
||||
function SomeTitleComponent () {
|
||||
return (
|
||||
<Typography variant="Title/lg">
|
||||
<h1>A title with proper typography</h1>
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
It also takes care of merging with any class already present.
|
||||
|
||||
```
|
||||
import { Typography } from '@scandic-hotels/design-system'
|
||||
|
||||
import styles from './my-component.module.css'
|
||||
|
||||
function MyComponent () {
|
||||
return (
|
||||
<Typography variant="Title/lg">
|
||||
<h1 className={styles.title}>A title with proper typography</h1>
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
<Subtitle>The higher order function</Subtitle>
|
||||
|
||||
> The approach described in this section is platform agnostic. However this documentation is for the implementation aimed at web. The same can be achived for other platforms but currently it is only implemented for web.
|
||||
|
||||
The higher order function for typography implements all the Figma styles for typography with Class Variance Authority. The implementation merges any components CVA variants with the CVA variants for typography. It does this by exporting a higher order function named `withTypography`.
|
||||
|
||||
The higher order function named `withTypography` accepts a CVA config and returns a new CVA config enhanced with a `typography` variant. This will give the component implementing the CVA config a prop named `typography`. This prop works the same as the `variant` prop for the `Typography` component with handling merging of classes already preset, code completion and everything.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
// variants.ts
|
||||
import { cva } from 'class-variance-authority'
|
||||
|
||||
import { withTypography } from '@scandic-hotels/design-system'
|
||||
|
||||
import styles from './my-component.module.css'
|
||||
|
||||
export const config = {
|
||||
variants: {
|
||||
...
|
||||
},
|
||||
defaultVariants: {
|
||||
...
|
||||
},
|
||||
} as const
|
||||
|
||||
export const variants = cva(styles.myComponent, withTypography(config))
|
||||
```
|
||||
|
||||
```
|
||||
// MyComponent.tsx
|
||||
import { variants } from './variants'
|
||||
|
||||
export function MyComponent (props) {
|
||||
const classNames = variants(...)
|
||||
return <div {...props} className={classNames} />
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
// OtherComponent.tsx
|
||||
...
|
||||
<MyComponent typography="Title/lg">
|
||||
...
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
The component accepts the following props.
|
||||
|
||||
> For the component to work the nested child component needs to handle `className` prop.
|
||||
|
||||
<Controls />
|
||||
|
||||
---
|
||||
|
||||
## All typography styles
|
||||
|
||||
<Stories />
|
||||
@@ -0,0 +1,189 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import { Typography } from './Typography.tsx'
|
||||
|
||||
import TypographyDocs from './Typography.docs.mdx'
|
||||
|
||||
import { config as typographyConfig } from './variants'
|
||||
|
||||
const meta: Meta<typeof Typography> = {
|
||||
title: 'Components/Typography',
|
||||
component: Typography,
|
||||
args: { variant: typographyConfig.defaultVariants.variant },
|
||||
argTypes: {
|
||||
variant: {
|
||||
control: 'select',
|
||||
options: Object.keys(typographyConfig.variants.variant),
|
||||
table: {
|
||||
defaultValue: { summary: typographyConfig.defaultVariants.variant },
|
||||
},
|
||||
},
|
||||
},
|
||||
parameters: { docs: { toc: true, page: TypographyDocs } },
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
type Story = StoryObj<typeof Typography>
|
||||
|
||||
export const TitleLg: Story = {
|
||||
name: 'Title/lg',
|
||||
args: {
|
||||
variant: 'Title/lg',
|
||||
children: <h1>The quick brown fox jumps over the lazy dog</h1>,
|
||||
},
|
||||
}
|
||||
export const TitleMd: Story = {
|
||||
name: 'Title/md',
|
||||
args: {
|
||||
variant: 'Title/md',
|
||||
children: <h1>The quick brown fox jumps over the lazy dog</h1>,
|
||||
},
|
||||
}
|
||||
export const TitleMdRegular: Story = {
|
||||
name: 'Title/mdRegular',
|
||||
args: {
|
||||
variant: 'Title/mdRegular',
|
||||
children: <h1>The quick brown fox jumps over the lazy dog</h1>,
|
||||
},
|
||||
}
|
||||
export const TitleSm: Story = {
|
||||
name: 'Title/sm',
|
||||
args: {
|
||||
variant: 'Title/sm',
|
||||
children: <h1>The quick brown fox jumps over the lazy dog</h1>,
|
||||
},
|
||||
}
|
||||
export const TitleSmRegular: Story = {
|
||||
name: 'Title/smRegular',
|
||||
args: {
|
||||
variant: 'Title/smRegular',
|
||||
children: <h1>The quick brown fox jumps over the lazy dog</h1>,
|
||||
},
|
||||
}
|
||||
export const TitleXs: Story = {
|
||||
name: 'Title/xs',
|
||||
args: {
|
||||
variant: 'Title/xs',
|
||||
children: <h1>The quick brown fox jumps over the lazy dog</h1>,
|
||||
},
|
||||
}
|
||||
export const TitleDecorativeLg: Story = {
|
||||
name: 'Title/Decorative/lg',
|
||||
args: {
|
||||
variant: 'Title/Decorative/lg',
|
||||
children: <h1>The quick brown fox jumps over the lazy dog</h1>,
|
||||
},
|
||||
}
|
||||
export const TitleDecorativeMd: Story = {
|
||||
name: 'Title/Decorative/md',
|
||||
args: {
|
||||
variant: 'Title/Decorative/md',
|
||||
children: <h1>The quick brown fox jumps over the lazy dog</h1>,
|
||||
},
|
||||
}
|
||||
export const TitleSubtitleLg: Story = {
|
||||
name: 'Title/Subtitle/lg',
|
||||
args: {
|
||||
variant: 'Title/Subtitle/lg',
|
||||
children: <h1>The quick brown fox jumps over the lazy dog</h1>,
|
||||
},
|
||||
}
|
||||
export const TitleSubtitleMd: Story = {
|
||||
name: 'Title/Subtitle/md',
|
||||
args: {
|
||||
variant: 'Title/Subtitle/md',
|
||||
children: <h1>The quick brown fox jumps over the lazy dog</h1>,
|
||||
},
|
||||
}
|
||||
export const TitleOverlineSm: Story = {
|
||||
name: 'Title/Overline/sm',
|
||||
args: {
|
||||
variant: 'Title/Overline/sm',
|
||||
children: <h1>The quick brown fox jumps over the lazy dog</h1>,
|
||||
},
|
||||
}
|
||||
export const BodyLeadtext: Story = {
|
||||
name: 'Body/Lead text',
|
||||
args: {
|
||||
variant: 'Body/Lead text',
|
||||
children: <p>The quick brown fox jumps over the lazy dog</p>,
|
||||
},
|
||||
}
|
||||
export const BodyParagraphMdRegular: Story = {
|
||||
name: 'Body/Paragraph/mdRegular',
|
||||
args: {
|
||||
variant: 'Body/Paragraph/mdRegular',
|
||||
children: <p>The quick brown fox jumps over the lazy dog</p>,
|
||||
},
|
||||
}
|
||||
export const BodyParagraphMdBold: Story = {
|
||||
name: 'Body/Paragraph/mdBold',
|
||||
args: {
|
||||
variant: 'Body/Paragraph/mdBold',
|
||||
children: <p>The quick brown fox jumps over the lazy dog</p>,
|
||||
},
|
||||
}
|
||||
export const BodySupportingtextCaptionSmRegular: Story = {
|
||||
name: 'Body/Supporting text (caption)/smRegular',
|
||||
args: {
|
||||
variant: 'Body/Supporting text (caption)/smRegular',
|
||||
children: <p>The quick brown fox jumps over the lazy dog</p>,
|
||||
},
|
||||
}
|
||||
export const BodySupportingtextCaptionSmBold: Story = {
|
||||
name: 'Body/Supporting text (caption)/smBold',
|
||||
args: {
|
||||
variant: 'Body/Supporting text (caption)/smBold',
|
||||
children: <p>The quick brown fox jumps over the lazy dog</p>,
|
||||
},
|
||||
}
|
||||
export const BodyUnderlineMd: Story = {
|
||||
name: 'Body/Underline/md',
|
||||
args: {
|
||||
variant: 'Body/Underline/md',
|
||||
children: <p>The quick brown fox jumps over the lazy dog</p>,
|
||||
},
|
||||
}
|
||||
export const BodyUnderlineSm: Story = {
|
||||
name: 'Body/Underline/sm',
|
||||
args: {
|
||||
variant: 'Body/Underline/sm',
|
||||
children: <p>The quick brown fox jumps over the lazy dog</p>,
|
||||
},
|
||||
}
|
||||
export const TagSm: Story = {
|
||||
name: 'Tag/sm',
|
||||
args: {
|
||||
variant: 'Tag/sm',
|
||||
children: <span>The quick brown fox jumps over the lazy dog</span>,
|
||||
},
|
||||
}
|
||||
export const LinkMd: Story = {
|
||||
name: 'Link/md',
|
||||
args: {
|
||||
variant: 'Link/md',
|
||||
children: <a href="#">The quick brown fox jumps over the lazy dog</a>,
|
||||
},
|
||||
}
|
||||
export const LinkSm: Story = {
|
||||
name: 'Link/sm',
|
||||
args: {
|
||||
variant: 'Link/sm',
|
||||
children: <a href="#">The quick brown fox jumps over the lazy dog</a>,
|
||||
},
|
||||
}
|
||||
export const LabelXsRegular: Story = {
|
||||
name: 'Label/xsRegular',
|
||||
args: {
|
||||
variant: 'Label/xsRegular',
|
||||
children: <span>The quick brown fox jumps over the lazy dog</span>,
|
||||
},
|
||||
}
|
||||
export const LabelXsBold: Story = {
|
||||
name: 'Label/xsBold',
|
||||
args: {
|
||||
variant: 'Label/xsBold',
|
||||
children: <span>The quick brown fox jumps over the lazy dog</span>,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { cloneElement, isValidElement } from 'react'
|
||||
|
||||
import { variants } from './variants'
|
||||
|
||||
import type { TypographyProps } from './types'
|
||||
|
||||
export function Typography({ variant, children }: TypographyProps) {
|
||||
if (!isValidElement(children)) return null
|
||||
|
||||
const classNames = variants({
|
||||
variant,
|
||||
})
|
||||
|
||||
return cloneElement(children, {
|
||||
...children.props,
|
||||
className: [children.props.className, classNames].filter(Boolean).join(' '),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export { Typography } from './Typography'
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export { withTypography } from './variants'
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
|
||||
import type { variants } from './variants'
|
||||
|
||||
export interface TypographyProps
|
||||
extends Pick<React.HTMLAttributes<HTMLElement>, 'className'>,
|
||||
VariantProps<typeof variants> {
|
||||
children: React.ReactElement<{ className?: string }>
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
.Title-lg {
|
||||
font-family: var(--Title-lg-Font-family), var(--Title-lg-Font-fallback);
|
||||
font-size: var(--Title-lg-Size);
|
||||
font-weight: var(--Title-lg-Font-weight);
|
||||
letter-spacing: var(--Title-lg-Letter-spacing);
|
||||
text-transform: var(--Title-lg-Text-Transform);
|
||||
line-height: 1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Title-md {
|
||||
font-family: var(--Title-md-Font-family), var(--Title-md-Font-fallback);
|
||||
font-size: var(--Title-md-Size);
|
||||
font-weight: var(--Title-md-Font-weight);
|
||||
letter-spacing: var(--Title-md-Letter-spacing);
|
||||
text-transform: var(--Title-md-Text-Transform);
|
||||
line-height: 1.1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Title-mdRegular {
|
||||
font-family: var(--Title-md-Font-family), var(--Title-md-Font-fallback);
|
||||
font-size: var(--Title-md-Size);
|
||||
font-weight: 700;
|
||||
letter-spacing: var(--Title-md-Letter-spacing);
|
||||
text-transform: var(--Title-md-Text-Transform);
|
||||
line-height: 1.1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Title-sm {
|
||||
font-family: var(--Title-sm-Font-family), var(--Title-sm-Font-fallback);
|
||||
font-size: var(--Title-sm-Size);
|
||||
font-weight: var(--Title-sm-Font-weight);
|
||||
letter-spacing: var(--Title-sm-Letter-spacing);
|
||||
text-transform: var(--Title-sm-Text-Transform);
|
||||
line-height: 1.1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Title-smRegular {
|
||||
font-family:
|
||||
var(--Title-sm-LowCase-Font-family), var(--Title-sm-LowCase-Font-fallback);
|
||||
font-size: var(--Title-sm-LowCase-Size);
|
||||
font-weight: var(--Title-sm-LowCase-Font-weight);
|
||||
letter-spacing: var(--Title-sm-LowCase-Letter-spacing);
|
||||
text-transform: var(--Title-sm-LowCase-Text-Transform);
|
||||
line-height: 1.1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Title-xs {
|
||||
font-family: var(--Title-xs-Font-family), var(--Title-xs-Font-fallback);
|
||||
font-size: var(--Title-xs-Size);
|
||||
font-weight: var(--Title-xs-Font-weight);
|
||||
letter-spacing: var(--Title-xs-Letter-spacing);
|
||||
text-transform: var(--Title-xs-Text-Transform);
|
||||
line-height: 1.1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Title-Decorative-lg {
|
||||
font-family:
|
||||
var(--Title-Decorative-lg-Font-family),
|
||||
var(--Title-Decorative-lg-Font-fallback);
|
||||
font-size: var(--Title-Decorative-lg-Size);
|
||||
font-weight: var(--Title-Decorative-lg-Font-weight);
|
||||
letter-spacing: var(--Title-Decorative-lg-Letter-spacing);
|
||||
text-transform: var(--Title-Decorative-lg-Text-Transform);
|
||||
line-height: 1.1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Title-Decorative-md {
|
||||
font-family:
|
||||
var(--Title-Decorative-md-Font-family),
|
||||
var(--Title-Decorative-md-Font-fallback);
|
||||
font-size: var(--Title-Decorative-md-Size);
|
||||
font-weight: var(--Title-Decorative-md-Font-weight);
|
||||
letter-spacing: var(--Title-Decorative-md-Letter-spacing);
|
||||
text-transform: var(--Title-Decorative-md-Text-Transform);
|
||||
line-height: 1.1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Title-Subtitle-lg {
|
||||
font-family:
|
||||
var(--Title-Subtitle-lg-Font-family), var(--Title-Subtitle-lg-Font-fallback);
|
||||
font-size: var(--Title-Subtitle-lg-Size);
|
||||
font-weight: var(--Title-Subtitle-lg-Font-weight);
|
||||
letter-spacing: var(--Title-Subtitle-lg-Letter-spacing);
|
||||
text-transform: unset;
|
||||
line-height: 1.2;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Title-Subtitle-md {
|
||||
font-family:
|
||||
var(--Title-Subtitle-md-Font-family), var(--Title-Subtitle-md-Font-fallback);
|
||||
font-size: var(--Title-Subtitle-md-Size);
|
||||
font-weight: var(--Title-Subtitle-md-Font-weight);
|
||||
letter-spacing: var(--Title-Subtitle-md-Letter-spacing);
|
||||
text-transform: unset;
|
||||
line-height: 1.2;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Title-Overline-sm {
|
||||
font-family:
|
||||
var(--Title-Overline-sm-Font-family), var(--Title-Overline-sm-Font-fallback);
|
||||
font-size: var(--Title-Overline-sm-Size);
|
||||
font-weight: var(--Title-Overline-sm-Font-weight);
|
||||
letter-spacing: var(--Title-Overline-sm-Letter-spacing);
|
||||
text-transform: var(--Title-Overline-sm-Text-Transform);
|
||||
line-height: 1.5;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Body-Lead-text {
|
||||
font-family:
|
||||
var(--Body-Lead-text-Font-family), var(--Body-Lead-text-Font-fallback);
|
||||
font-size: var(--Body-Lead-text-Size);
|
||||
font-weight: var(--Body-Lead-text-Font-weight);
|
||||
letter-spacing: var(--Body-Lead-text-Letter-spacing);
|
||||
text-transform: unset;
|
||||
line-height: 1.5;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Body-Paragraph-mdRegular {
|
||||
font-family:
|
||||
var(--Body-Paragraph-Font-family), var(--Body-Paragraph-Font-fallback);
|
||||
font-size: var(--Body-Paragraph-Size);
|
||||
font-weight: var(--Body-Paragraph-Font-weight);
|
||||
letter-spacing: var(--Body-Paragraph-Letter-spacing);
|
||||
text-transform: unset;
|
||||
line-height: 1.5;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Body-Paragraph-mdBold {
|
||||
font-family:
|
||||
var(--Body-Paragraph-Font-family), var(--Body-Paragraph-Font-fallback);
|
||||
font-size: var(--Body-Paragraph-Size);
|
||||
font-weight: var(--Body-Paragraph-Font-weight-2);
|
||||
letter-spacing: var(--Body-Paragraph-Letter-spacing);
|
||||
text-transform: unset;
|
||||
line-height: 1.5;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Body-Supporting-text-caption-smRegular {
|
||||
font-family:
|
||||
var(--Body-Supporting-text-Font-family),
|
||||
var(--Body-Supporting-text-Font-fallback);
|
||||
font-size: var(--Body-Supporting-text-Size);
|
||||
font-weight: var(--Body-Supporting-text-Font-weight);
|
||||
letter-spacing: var(--Body-Supporting-text-Letter-spacing);
|
||||
text-transform: unset;
|
||||
line-height: 1.4;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Body-Supporting-text-caption-smBold {
|
||||
font-family:
|
||||
var(--Body-Supporting-text-Font-family),
|
||||
var(--Body-Supporting-text-Font-fallback);
|
||||
font-size: var(--Body-Supporting-text-Size);
|
||||
font-weight: var(--Body-Supporting-text-Font-weight-2);
|
||||
letter-spacing: var(--Body-Supporting-text-Letter-spacing);
|
||||
text-transform: unset;
|
||||
line-height: 1.4;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Body-Underline-md {
|
||||
font-family:
|
||||
var(--Body-Underline-md-Font-family), var(--Body-Underline-md-Font-fallback);
|
||||
font-size: var(--Body-Underline-md-Size);
|
||||
font-weight: var(--Body-Underline-md-Font-weight);
|
||||
letter-spacing: var(--Body-Underline-md-Letter-spacing);
|
||||
text-transform: unset;
|
||||
line-height: 1.5;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.Body-Underline-sm {
|
||||
font-family:
|
||||
var(--Body-Underline-sm-Font-family), var(--Body-Underline-sm-Font-fallback);
|
||||
font-size: var(--Body-Underline-sm-Size);
|
||||
font-weight: var(--Body-Underline-sm-Font-weight);
|
||||
letter-spacing: var(--Body-Underline-sm-Letter-spacing);
|
||||
text-transform: unset;
|
||||
line-height: 1.4;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.Tag-sm {
|
||||
font-family: var(--Tag-Font-family), var(--Tag-Font-fallback);
|
||||
font-size: var(--Tag-Size);
|
||||
font-weight: var(--Tag-Font-weight);
|
||||
letter-spacing: var(--Tag-Letter-spacing);
|
||||
text-transform: var(--Tag-Text-Transform);
|
||||
line-height: 1.5;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Link-md {
|
||||
font-family: var(--Link-md-Font-family), var(--Link-md-Font-fallback);
|
||||
font-size: var(--Link-md-Size);
|
||||
font-weight: var(--Link-md-Font-weight);
|
||||
letter-spacing: var(--Link-md-Letter-spacing);
|
||||
text-transform: unset;
|
||||
line-height: 1.5;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.Link-sm {
|
||||
font-family: var(--Link-sm-Font-family), var(--Link-sm-Font-fallback);
|
||||
font-size: var(--Link-sm-Size);
|
||||
font-weight: var(--Link-sm-Font-weight);
|
||||
letter-spacing: var(--Link-sm-Letter-spacing);
|
||||
text-transform: unset;
|
||||
line-height: 1.4;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.Label-xsRegular {
|
||||
font-family: var(--Label-Font-family), var(--Label-Font-fallback);
|
||||
font-size: var(--Label-Size);
|
||||
font-weight: var(--Label-Font-weight);
|
||||
letter-spacing: var(--Label-Letter-spacing);
|
||||
text-transform: unset;
|
||||
line-height: 1.5;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Label-xsBold {
|
||||
font-family: var(--Label-Font-family), var(--Label-Font-fallback);
|
||||
font-size: var(--Label-Size);
|
||||
font-weight: var(--Label-Font-weight-2);
|
||||
letter-spacing: var(--Label-Letter-spacing);
|
||||
text-transform: unset;
|
||||
line-height: 1.5;
|
||||
text-decoration: none;
|
||||
}
|
||||
52
packages/design-system/lib/components/Typography/variants.ts
Normal file
52
packages/design-system/lib/components/Typography/variants.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { cva } from 'class-variance-authority'
|
||||
import { deepmerge } from 'deepmerge-ts'
|
||||
|
||||
import styles from './typography.module.css'
|
||||
|
||||
export const config = {
|
||||
variants: {
|
||||
variant: {
|
||||
'Title/lg': styles['Title-lg'],
|
||||
'Title/md': styles['Title-md'],
|
||||
'Title/mdRegular': styles['Title-mdRegular'],
|
||||
'Title/sm': styles['Title-sm'],
|
||||
'Title/smRegular': styles['Title-smRegular'],
|
||||
'Title/xs': styles['Title-xs'],
|
||||
'Title/Decorative/lg': styles['Title-Decorative-lg'],
|
||||
'Title/Decorative/md': styles['Title-Decorative-md'],
|
||||
'Title/Subtitle/lg': styles['Title-Subtitle-lg'],
|
||||
'Title/Subtitle/md': styles['Title-Subtitle-md'],
|
||||
'Title/Overline/sm': styles['Title-Overline-sm'],
|
||||
'Body/Lead text': styles['Body-Lead-text'],
|
||||
'Body/Paragraph/mdRegular': styles['Body-Paragraph-mdRegular'],
|
||||
'Body/Paragraph/mdBold': styles['Body-Paragraph-mdBold'],
|
||||
'Body/Supporting text (caption)/smRegular':
|
||||
styles['Body-Supporting-text-caption-smRegular'],
|
||||
'Body/Supporting text (caption)/smBold':
|
||||
styles['Body-Supporting-text-caption-smBold'],
|
||||
'Body/Underline/md': styles['Body-Underline-md'],
|
||||
'Body/Underline/sm': styles['Body-Underline-sm'],
|
||||
'Tag/sm': styles['Tag-sm'],
|
||||
'Link/md': styles['Link-md'],
|
||||
'Link/sm': styles['Link-sm'],
|
||||
'Label/xsRegular': styles['Label-xsRegular'],
|
||||
'Label/xsBold': styles['Label-xsBold'],
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'Body/Paragraph/mdRegular',
|
||||
},
|
||||
} as const
|
||||
|
||||
export const variants = cva({}, config)
|
||||
|
||||
const typographyConfig = {
|
||||
variants: {
|
||||
typography: config.variants.variant,
|
||||
},
|
||||
defaultVariants: config.defaultVariants,
|
||||
} as const
|
||||
|
||||
export function withTypography<T>(config: T) {
|
||||
return deepmerge(typographyConfig, config)
|
||||
}
|
||||
Reference in New Issue
Block a user