Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add descriptions to semantic tokens #69

Merged
merged 2 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions docs/app/preset/colors/[name]/components/color-details.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import type { SemanticToken, Sentiment } from '@cerberus-design/panda-preset'
import { container, grid, gridItem, vstack } from '@/styled-system/patterns'
import { normalizeTokens, getTokenList } from '../../helpers/normalize'
import { css } from '@/styled-system/css'

const PREVIEW_SIZE = '15rem'

interface ColorDetailsProps {
token: string
}

export default function ColorDetails(props: ColorDetailsProps) {
const palette = props.token.split('-')[0] as Sentiment
const tokens = normalizeTokens(getTokenList(palette), palette)
const token = tokens[props.token as keyof typeof tokens] as SemanticToken
const semTokenValue = props.token.split('-').join('.')
const swatchColor = {
backgroundColor: token.value._cerberusTheme.base,
}

return (
<div className={container()}>
<section
className={grid({
columns: 12,
lg: {
mb: '8',
pb: '8',
},
})}
>
<div
className={gridItem({
gridColumnStart: 1,
gridColumnEnd: 13,
lg: {
gridColumnStart: 1,
gridColumnEnd: 5,
},
})}
>
<div
className={vstack({
border: '3px solid',
borderColor: 'neutral.border.initial',
justify: 'center',
bgColor: 'neutral.surface.200',
h: PREVIEW_SIZE,
rounded: '2xl',
})}
style={swatchColor}
/>
</div>

<div
className={gridItem({
gridColumnStart: 1,
gridColumnEnd: 13,
lg: {
gridColumnStart: 5,
paddingInlineStart: '4',
},
})}
>
<h2>{props.token}</h2>
<p>{token.description}</p>
<p>
Hex: <code>{token.value._cerberusTheme.base}</code>
</p>
<p>
Token: <code>{semTokenValue}</code>
</p>
</div>
</section>
</div>
)
}
23 changes: 23 additions & 0 deletions docs/app/preset/colors/[name]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import OnThisPage from '../../../components/OnThisPage'
import { PageMainContent, PageSections } from '../../../components/PageLayout'
import ColorDetails from './components/color-details'

interface ColorDetailsPageProps {
params: {
name: string
}
}

export default function ColorDetailsPage(props: ColorDetailsPageProps) {
return (
<>
<PageMainContent>
<ColorDetails token={props.params.name} />
</PageMainContent>

<PageSections>
<OnThisPage />
</PageSections>
</>
)
}
39 changes: 20 additions & 19 deletions docs/app/preset/colors/components/ColorSwatch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,12 @@ import { css, cx } from '@/styled-system/css'
import { hstack, vstack } from '@/styled-system/patterns'
import { Checkmark } from '@cerberus-design/icons'
import { hasWhiteBase } from '@/app/utils/colors'

// TODO: Figure out how to validate color contrast a11y for text on background

interface ColorSwatchProps {
token: SemanticToken
tokenName: string
palette: Sentiment
}
import Link from 'next/link'

// We have to use !important to override the .MDX styles
const paletteTextStyles = css({
textAlign: 'center',

'&[data-palette="neutral"]': {
color: 'neutral.text.initial !important',
'&[data-has-white="true"]': {
Expand Down Expand Up @@ -80,10 +75,20 @@ const noPB = css({
textStyle: 'body-sm !important',
})

interface ColorSwatchProps {
token: SemanticToken
tokenName: string
palette: Sentiment
}

export default function ColorSwatch(props: ColorSwatchProps) {
const { _cerberusTheme } = props.token.value
const { mode } = useThemeContext()
const [copied, setCopied] = useState<boolean>(false)
const objName = useMemo(
() => props.tokenName.replace(/-/g, '.'),
[props.tokenName],
)

const modeValue = useMemo(() => {
return mode === 'dark' ? '_darkMode' : '_lightMode'
Expand All @@ -94,11 +99,6 @@ export default function ColorSwatch(props: ColorSwatchProps) {
backgroundColor: color,
}

function handleCopyToClipboard() {
navigator.clipboard.writeText(props.tokenName)
setCopied(true)
}

useEffect(() => {
if (copied) {
const timeout = setTimeout(() => {
Expand All @@ -109,19 +109,22 @@ export default function ColorSwatch(props: ColorSwatchProps) {
}, [copied])

return (
<button
<Link
className={vstack({
alignItems: 'center',
justifyContent: 'center',
h: '10rem',
gap: '4',
rounded: 'xl',
textDecoration: 'none',
transition: 'scale 250ms ease-in-out',
w: 'full',
_hover: {
scale: '105%',
textDecoration: 'none !important',
},
})}
onClick={handleCopyToClipboard}
href={`/preset/colors/${props.tokenName}`}
style={bgColor}
>
{copied && (
Expand All @@ -146,7 +149,7 @@ export default function ColorSwatch(props: ColorSwatchProps) {
data-has-white={isWhite}
className={paletteTextStyles}
>
<p className={noPB}>{props.tokenName}</p>
<p className={noPB}>{objName}</p>
<p
className={cx(
css({
Expand All @@ -158,8 +161,6 @@ export default function ColorSwatch(props: ColorSwatchProps) {
{color}
</p>
</div>

{/* <AccessibilityAlt aria-hidden /> */}
</button>
</Link>
)
}
85 changes: 3 additions & 82 deletions docs/app/preset/colors/components/PaletteList.tsx
Original file line number Diff line number Diff line change
@@ -1,87 +1,8 @@
import {
type Sentiment,
neutralTokens,
actionTokens,
infoTokens,
successTokens,
warningTokens,
dangerTokens,
type SentimentConfig,
type SemanticToken,
} from '@cerberus-design/panda-preset'
import { type Sentiment } from '@cerberus-design/panda-preset'
import ColorSwatch from './ColorSwatch'
import { css } from '@/styled-system/css'
import { grid, gridItem } from '@/styled-system/patterns'

function getTokenList(palette: Sentiment): SentimentConfig[Sentiment] {
switch (palette) {
case 'neutral':
return neutralTokens.neutral
case 'action':
return actionTokens.action
case 'info':
return infoTokens.info
case 'success':
return successTokens.success
case 'warning':
return warningTokens.warning
case 'danger':
return dangerTokens.danger
default:
throw new Error('Invalid color palette')
}
}

function normalizeTokens(
tokens: SentimentConfig[Sentiment],
palette: Sentiment,
) {
const usage = Object.keys(tokens!)
return usage.reduce((acc, key) => {
const token = tokens![key as keyof typeof tokens]
const tokenKeys = Object.keys(token!)
const nestedTokenKeys = tokenKeys.filter(
(tokenKey) => typeof token![tokenKey as keyof typeof token] === 'object',
)

const nestedTokens = normalizeNestedTokens({
nestedTokenKeys,
token,
key,
palette,
})

return { ...acc, ...nestedTokens }
}, {})
}

interface NormalizeNestedTokensProps {
nestedTokenKeys: string[]
token: SemanticToken
key: string
palette: Sentiment
}

function normalizeNestedTokens(data: NormalizeNestedTokensProps) {
const { token, key, palette } = data
return data.nestedTokenKeys.reduce((acc, tokenKey) => {
const nestedToken: Object = token![tokenKey as keyof typeof token]

if (nestedToken.hasOwnProperty('value'))
return { ...acc, [`${palette}-${key}-${tokenKey}`]: nestedToken }

const nestedTokenKeys = Object.keys(nestedToken)
const normalizedNestedToken = nestedTokenKeys.reduce(
(acc, nestedTokenKey) => {
const value = nestedToken[nestedTokenKey as keyof typeof nestedToken]
const tokenName = `${palette}-${key}-${tokenKey}-${nestedTokenKey}`
return { ...acc, [tokenName]: value }
},
{},
)
return { ...acc, ...normalizedNestedToken }
}, {})
}
import { normalizeTokens, getTokenList } from '../helpers/normalize'

interface PaletteListProps {
palette: Sentiment
Expand Down Expand Up @@ -115,7 +36,7 @@ export default function PaletteList(props: PaletteListProps) {
<ColorSwatch
palette={palette}
token={tokens[token as keyof typeof tokens]}
tokenName={token.replace(/-/g, '.')}
tokenName={token}
/>
</li>
))}
Expand Down
82 changes: 82 additions & 0 deletions docs/app/preset/colors/helpers/normalize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import {
actionTokens,
dangerTokens,
infoTokens,
neutralTokens,
successTokens,
warningTokens,
type SemanticToken,
type Sentiment,
type SentimentConfig,
type ThemeSelectors,
} from '@cerberus-design/panda-preset'

export function getTokenList(palette: Sentiment): SentimentConfig[Sentiment] {
switch (palette) {
case 'neutral':
return neutralTokens.neutral
case 'action':
return actionTokens.action
case 'info':
return infoTokens.info
case 'success':
return successTokens.success
case 'warning':
return warningTokens.warning
case 'danger':
return dangerTokens.danger
default:
throw new Error('Invalid color palette')
}
}

export function normalizeTokens(
tokens: SentimentConfig[Sentiment],
palette: Sentiment,
) {
const usage = Object.keys(tokens!)
return usage.reduce((acc, key) => {
const token = tokens![key as keyof typeof tokens]
const tokenKeys = Object.keys(token!)
const nestedTokenKeys = tokenKeys.filter(
(tokenKey) => typeof token![tokenKey as keyof typeof token] === 'object',
)

const nestedTokens = normalizeNestedTokens({
nestedTokenKeys,
token,
key,
palette,
})

return { ...acc, ...nestedTokens }
}, {})
}

interface NormalizeNestedTokensProps {
nestedTokenKeys: string[]
token: SemanticToken
key: string
palette: Sentiment
}

export function normalizeNestedTokens(data: NormalizeNestedTokensProps) {
const { token, key, palette } = data
return data.nestedTokenKeys.reduce((acc, tokenKey) => {
const nestedToken = token![tokenKey as keyof typeof token] as ThemeSelectors

if (nestedToken.hasOwnProperty('value'))
return { ...acc, [`${palette}-${key}-${tokenKey}`]: nestedToken }

const nestedTokenKeys = Object.keys(nestedToken)
const normalizedNestedToken = nestedTokenKeys.reduce(
(acc, nestedTokenKey) => {
const value = nestedToken[nestedTokenKey as keyof typeof nestedToken]
const tokenName = `${palette}-${key}-${tokenKey}-${nestedTokenKey}`
return { ...acc, [tokenName]: value }
},
{},
)
return { ...acc, ...normalizedNestedToken }
}, {})
}
Loading