Skip to content

Commit

Permalink
Merge pull request #69 from omnifed/68-docs-add-color-swatch-details-…
Browse files Browse the repository at this point in the history
…page

feat: add descriptions to semantic tokens
  • Loading branch information
caseybaggz committed May 17, 2024
2 parents 020d686 + 8fa0b2c commit 5ff88b2
Show file tree
Hide file tree
Showing 13 changed files with 302 additions and 107 deletions.
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

0 comments on commit 5ff88b2

Please sign in to comment.