Skip to content

refactor(typography): apply vanilla-extract #162

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

Open
wants to merge 6 commits into
base: release/v1
Choose a base branch
from
Open
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
6 changes: 3 additions & 3 deletions packages/typography/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
},
"type": "module",
"exports": "./src/index.ts",
"files": [
"dist"
],
"files": ["dist"],
"scripts": {
"build": "tsup",
"build:storybook": "storybook build",
Expand All @@ -25,6 +23,7 @@
"dependencies": {
"@radix-ui/react-slot": "^1.1.0",
"@sipe-team/tokens": "workspace:^",
"@vanilla-extract/dynamic": "^2.1.3",
"clsx": "^2.1.1"
},
"devDependencies": {
Expand All @@ -39,6 +38,7 @@
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.0.1",
"@types/react": "^18.3.12",
"@vanilla-extract/css": "catalog:",
"happy-dom": "catalog:",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down
27 changes: 27 additions & 0 deletions packages/typography/src/Typography.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { fontSize, fontWeight, lineHeight } from '@sipe-team/tokens';
import { style, styleVariants, createVar } from '@vanilla-extract/css';

export const textColorVar = createVar();

export const base = style({
margin: 0,
color: textColorVar,
});

export const size = styleVariants(fontSize, (value) => ({
fontSize: `${value}px`,
}));
Comment on lines +11 to +13
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 callback으로 값 변환이 가능하구나!


export const weight = styleVariants(fontWeight, (value) => ({
fontWeight: value,
}));

export const lineHeightVariants = styleVariants(lineHeight, (value) => ({
lineHeight: value,
}));

export type TypographyVariants = {
size?: keyof typeof size;
weight?: keyof typeof weight;
lineHeight?: keyof typeof lineHeightVariants;
};
6 changes: 0 additions & 6 deletions packages/typography/src/Typography.module.css

This file was deleted.

196 changes: 168 additions & 28 deletions packages/typography/src/Typography.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { fontSize, fontWeight, lineHeight } from '@sipe-team/tokens';
import type { Meta, StoryObj } from '@storybook/react';
import { Typography } from './Typography';

Expand All @@ -7,6 +8,36 @@ const meta = {
parameters: {
layout: 'centered',
},
argTypes: {
weight: {
control: 'select',
options: Object.keys(fontWeight),
description: '글꼴 굵기',
},
size: {
control: 'select',
options: Object.keys(fontSize),
description: '글꼴 크기',
},
lineHeight: {
control: 'select',
options: Object.keys(lineHeight),
description: '줄 높이',
},
color: {
control: 'color',
description: '글꼴 색상',
},
as: {
control: 'select',
options: ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'span', 'div'],
description: '렌더링할 HTML 요소',
},
asChild: {
control: 'boolean',
description: '자식 요소의 태그를 사용',
},
},
} satisfies Meta<typeof Typography>;
export default meta;

Expand All @@ -15,35 +46,144 @@ type Story = StoryObj<typeof meta>;
export const Basic: Story = {
render() {
return (
<section>
<Typography asChild={true} size={32} weight="bold">
<h1>Weight</h1>
</Typography>
<Typography weight="regular">[기본 값] regular(400) - 사이프 디자인 시스템</Typography>
<Typography weight="medium">medium(500) - 사이프 디자인 시스템</Typography>
<Typography weight="semiBold">semiBold(600) - 사이프 디자인 시스템</Typography>
<Typography weight="bold">bold(700) - 사이프 디자인 시스템</Typography>

<Typography asChild={true} size={32} weight="bold">
<h1>Size</h1>
</Typography>
<Typography size={12}>12 - 사이프 디자인 시스템</Typography>
<Typography size={14}>[기본 값] 14 - 사이프 디자인 시스템</Typography>
<Typography size={16}>16 - 사이프 디자인 시스템</Typography>
<Typography size={18}>18 - 사이프 디자인 시스템</Typography>
<Typography size={20}>20 - 사이프 디자인 시스템</Typography>
<Typography size={24}>24 - 사이프 디자인 시스템</Typography>
<Typography size={28}>28 - 사이프 디자인 시스템</Typography>
<Typography size={32}>32 - 사이프 디자인 시스템</Typography>
<Typography size={36}>36 - 사이프 디자인 시스템</Typography>
<Typography size={48}>48 - 사이프 디자인 시스템</Typography>

<Typography asChild={true} size={32} weight="bold">
<h1>Line Height</h1>
</Typography>
<Typography lineHeight="regular">[기본 값] regular(1.5) - 사이프 디자인 시스템</Typography>
<Typography lineHeight="compact">compact(1.3) - 사이프 디자인 시스템</Typography>
<section style={{ maxWidth: '600px' }}>
<Typography as="h1" size={32} weight="bold" style={{ marginBottom: '24px' }}>
Typography
</Typography>

<Typography as="h2" size={24} weight="bold" style={{ marginBottom: '16px' }}>
Weight
</Typography>
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px', marginBottom: '24px' }}>
<Typography weight="regular">[기본 값] regular(400) - 사이프 디자인 시스템</Typography>
<Typography weight="medium">medium(500) - 사이프 디자인 시스템</Typography>
<Typography weight="semiBold">semiBold(600) - 사이프 디자인 시스템</Typography>
<Typography weight="bold">bold(700) - 사이프 디자인 시스템</Typography>
</div>

<Typography as="h2" size={24} weight="bold" style={{ marginBottom: '16px' }}>
Size
</Typography>
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px', marginBottom: '24px' }}>
<Typography size={12}>12 - 사이프 디자인 시스템</Typography>
<Typography size={14}>[기본 값] 14 - 사이프 디자인 시스템</Typography>
<Typography size={16}>16 - 사이프 디자인 시스템</Typography>
<Typography size={18}>18 - 사이프 디자인 시스템</Typography>
<Typography size={20}>20 - 사이프 디자인 시스템</Typography>
<Typography size={24}>24 - 사이프 디자인 시스템</Typography>
<Typography size={28}>28 - 사이프 디자인 시스템</Typography>
<Typography size={32}>32 - 사이프 디자인 시스템</Typography>
<Typography size={36}>36 - 사이프 디자인 시스템</Typography>
<Typography size={48}>48 - 사이프 디자인 시스템</Typography>
</div>

<Typography as="h2" size={24} weight="bold" style={{ marginBottom: '16px' }}>
Line Height
</Typography>
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
<div>
<Typography weight="semiBold">regular(1.5) - 기본값</Typography>
<Typography lineHeight="regular">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
ea commodo consequat.
</Typography>
</div>
<div>
<Typography weight="semiBold">compact(1.3)</Typography>
<Typography lineHeight="compact">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
ea commodo consequat.
</Typography>
</div>
</div>
</section>
);
},
};

export const ColorVariants: Story = {
render() {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
<Typography color="#FF0000">빨간색 텍스트</Typography>
<Typography color="#00FF00">녹색 텍스트</Typography>
<Typography color="#0000FF">파란색 텍스트</Typography>
<Typography color="#FF00FF">핑크색 텍스트</Typography>
<Typography color="rgba(0, 0, 0, 0.5)">반투명 텍스트</Typography>
</div>
);
},
};

export const SemanticElements: Story = {
render() {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
<Typography as="h1" size={32} weight="bold">
제목 1
</Typography>
<Typography as="h2" size={28} weight="bold">
제목 2
</Typography>
<Typography as="h3" size={24} weight="bold">
제목 3
</Typography>
<Typography as="h4" size={20} weight="semiBold">
제목 4
</Typography>
<Typography as="h5" size={18} weight="semiBold">
제목 5
</Typography>
<Typography as="h6" size={16} weight="semiBold">
제목 6
</Typography>
<Typography as="p" size={14}>
일반 텍스트 단락입니다.
</Typography>
<Typography as="span" size={14}>
인라인 텍스트입니다.
</Typography>
</div>
);
},
};

export const Polymorphic: Story = {
render() {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
<Typography weight="bold" size={20}>
기본 요소 (p)
</Typography>

<Typography
as="button"
style={{
cursor: 'pointer',
background: '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
padding: '8px 16px',
}}
>
버튼으로 렌더링
</Typography>

<Typography asChild>
<a href="https://github.com/sipe-team/side" style={{ color: '#007bff', textDecoration: 'underline' }}>
링크로 렌더링 (asChild)
</a>
</Typography>

<Typography asChild>
<label>
<input type="checkbox" /> 체크박스 라벨로 렌더링 (asChild)
</label>
</Typography>
</div>
);
},
};
36 changes: 19 additions & 17 deletions packages/typography/src/Typography.test.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
import { faker } from '@faker-js/faker';
import { fontWeight, lineHeight } from '@sipe-team/tokens';
import { render, screen, waitFor } from '@testing-library/react';
import { expect, test } from 'vitest';
import {
type FontSize,
type FontWeight,
type LineHeight,
Typography,
} from './Typography';
import { type FontSize, type FontWeight, type LineHeight, Typography } from './Typography';

test('weight를 주입하지 않으면 기본 값 regular(400)로 글꼴의 두께를 설정한다.', () => {
render(<Typography>테스트</Typography>);

expect(screen.getByText('테스트')).toHaveStyle({ fontWeight: 400 });
expect(screen.getByText('테스트')).toHaveStyle({ fontWeight: fontWeight.regular });
});

test.each([
{ weight: 'regular', numericWeight: 400 },
{ weight: 'medium', numericWeight: 500 },
{ weight: 'semiBold', numericWeight: 600 },
{ weight: 'bold', numericWeight: 700 },
{ weight: 'regular', numericWeight: fontWeight.regular },
{ weight: 'medium', numericWeight: fontWeight.medium },
{ weight: 'semiBold', numericWeight: fontWeight.semiBold },
{ weight: 'bold', numericWeight: fontWeight.bold },
] satisfies Array<{ weight: FontWeight; numericWeight: number }>)(
'주입한 $weight($numericWeight) weight을 기준으로 글꼴의 두께를 설정한다.',
({ weight, numericWeight }) => {
Expand Down Expand Up @@ -48,16 +44,16 @@ test.each([12, 14, 16, 18, 20, 24, 28, 32, 36, 48] satisfies FontSize[])(
test('lineHeight을 주입하지 않으면 기본 값 regular(1.5)로 줄 높이를 설정한다.', () => {
render(<Typography>테스트</Typography>);

expect(screen.getByText('테스트')).toHaveStyle({ lineHeight: 1.5 });
expect(screen.getByText('테스트')).toHaveStyle({ lineHeight: lineHeight.regular });
});

test.each([
{ lineHeight: 'regular', numericLineHeight: 1.5 },
{ lineHeight: 'compact', numericLineHeight: 1.3 },
{ lineHeight: 'regular', numericLineHeight: lineHeight.regular },
{ lineHeight: 'compact', numericLineHeight: lineHeight.compact },
] satisfies Array<{ lineHeight: LineHeight; numericLineHeight: number }>)(
'주입한 %s lineHeight을 기준으로 줄 높이를 설정한다.',
({ lineHeight, numericLineHeight }) => {
render(<Typography lineHeight={lineHeight}>테스트</Typography>);
({ lineHeight: lineHeightValue, numericLineHeight }) => {
render(<Typography lineHeight={lineHeightValue}>테스트</Typography>);

expect(screen.getByText('테스트')).toHaveStyle({
lineHeight: numericLineHeight,
Expand Down Expand Up @@ -93,12 +89,18 @@ test('asChild가 true일 때, children으로 전달된 요소에 Typography 스
expect(screen.getByText('테스트')).toHaveProperty('tagName', 'H1');
});

test('as prop을 통해 요소 타입을 변경할 수 있다.', () => {
render(<Typography as="h2">테스트</Typography>);

expect(screen.getByText('테스트')).toHaveProperty('tagName', 'H2');
});

test('className을 주입하면 추가로 전달한다.', () => {
const customClassName = faker.word.noun();

render(<Typography className={customClassName}>테스트</Typography>);

expect(screen.getByText('테스트')).toHaveClass(customClassName);
expect(screen.getByText('테스트').className).toContain(customClassName);
});

test('style을 주입하면 추가로 전달한다.', () => {
Expand Down
Loading