Skip to content

Commit

Permalink
feat: implement heading component
Browse files Browse the repository at this point in the history
  • Loading branch information
DaleSeo committed Jan 8, 2025
1 parent 010e541 commit 676d8f8
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 82 deletions.
24 changes: 21 additions & 3 deletions panda.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,33 @@ export default defineConfig({
"--font-spoqa": "Spoqa Han Sans Neo",
},

staticCss: {
css: [
{
properties: {
textStyle: Object.keys(textStyles),
fontSize: Object.keys(fontSizes),
fontWeight: Object.keys(fontWeights),
},
},
],
},

// Useful for theme customization
theme: {
extend: {
textStyles,
textStyles: Object.fromEntries(
Object.entries(textStyles).map(([key, value]) => [key, { value }])
),
tokens: {
colors,
fonts,
fontWeights,
fontSizes,
fontWeights: Object.fromEntries(
Object.entries(fontWeights).map(([key, value]) => [key, { value }])
),
fontSizes: Object.fromEntries(
Object.entries(fontSizes).map(([key, value]) => [key, { value }])
),
letterSpacings,
lineHeights,
},
Expand Down
48 changes: 48 additions & 0 deletions src/components/Heading/Heading.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type { Meta, StoryObj } from "@storybook/react";
import { vstack } from "../../../styled-system/patterns";
import { Heading } from "./Heading";

const meta = {
component: Heading,
parameters: {
layout: "centered",
},
args: {
children: "제목",
level: 2,
},
} satisfies Meta<typeof Heading>;

export default meta;

export const Basic: StoryObj<typeof meta> = {};

export const Levels: StoryObj<typeof meta> = {
render: (args) => {
return (
<div className={vstack({ gap: "6" })}>
<Heading {...args} level={1}>
1 단계
</Heading>
<Heading {...args} level={2}>
2 단계
</Heading>
<Heading {...args} level={3}>
3 단계
</Heading>
<Heading {...args} level={4}>
4 단계
</Heading>
<Heading {...args} level={5}>
5 단계
</Heading>
<Heading {...args} level={6}>
6 단계
</Heading>
</div>
);
},
parameters: {
controls: { disable: true },
},
};
20 changes: 20 additions & 0 deletions src/components/Heading/Heading.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { composeStories } from "@storybook/react";
import { render, screen } from "@testing-library/react";
import { expect, test } from "vitest";
import * as stories from "./Heading.stories";

const { Basic } = composeStories(stories);

test("renders the text content", () => {
render(<Basic>제목</Basic>);

const heading = screen.getByRole("heading");

expect(heading).toHaveTextContent("제목");
});

test.each([1, 2, 3, 4, 5, 6] as const)("use the correct level %s", (level) => {
render(<Basic level={level} />);

expect(screen.getByRole("heading", { level })).toBeInTheDocument();
});
61 changes: 61 additions & 0 deletions src/components/Heading/Heading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { css } from "../../../styled-system/css";
import type { TextStyle, FontSize, FontWeight } from "../../tokens/typography";

type Level = 1 | 2 | 3 | 4 | 5 | 6;

export interface HeadingProps extends React.HTMLAttributes<HTMLHeadingElement> {
/** 텍스트 */
children: React.ReactNode | string;
/** 단계 */
level?: Level;
/** 크기 */
size?: FontSize;
/** 굵기 */
weight?: FontWeight;
/** 명암비 */
// contrast?: "low" | "high";
}

const textStyles: Record<Level, TextStyle> = {
1: "4xl",
2: "3xl",
3: "2xl",
4: "xl",
5: "lg",
6: "md",
};

/**
* - `level` 속성을 통해서 `<h1>`, `<h2>`, `<h3>`, `<h4>`, `<h5>`, `<h6>` 요소 중 하나를 선택할 수 있습니다.
* - `level` 속성은 단계 별 기본 텍스트 스타일을 제공합니다.
* - `size` 속성과 `weight` 속성을 통해서 기본 스타일을 변경할 수 있습니다.
*/
export const Heading = ({
children,
level,
size,
weight,
// contrast = "low",
...rest
}: HeadingProps) => {
if (!level) {
throw new Error(
"The level prop is required and you can cause accessibility issues."
);
}

const Tag = `h${level}` as const;

return (
<Tag
className={css({
textStyle: textStyles[level],
fontSize: size,
fontWeight: weight,
})}
{...rest}
>
{children}
</Tag>
);
};
2 changes: 1 addition & 1 deletion src/index.css
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
@layer reset, base, tokens, recipes, utilities;
@import url(//spoqa.github.io/spoqa-han-sans/css/SpoqaHanSansNeo.css);
@import url(https://spoqa.github.io/spoqa-han-sans/css/SpoqaHanSansNeo.css);
4 changes: 2 additions & 2 deletions src/tokens/typography.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { fonts, fontWeights, fontSizes } from "./typography.ts";

<p>
{Object.entries(fontSizes)
.map(([name, { value }]) => `${name}(${value})`)
.map(([name, value]) => `${name}(${value})`)
.join(", ")}
</p>

Expand All @@ -22,7 +22,7 @@ import { fonts, fontWeights, fontSizes } from "./typography.ts";

## 글꼴 굵기

{Object.entries(fontWeights).map(([name, {value}]) => (
{Object.entries(fontWeights).map(([name, value]) => (

<>
<h3>
Expand Down
130 changes: 55 additions & 75 deletions src/tokens/typography.ts
Original file line number Diff line number Diff line change
@@ -1,116 +1,96 @@
import type { TextStyles, Tokens } from "@pandacss/types";
import type { Tokens } from "@pandacss/types";

export const textStyles: TextStyles = {
export const textStyles = {
xs: {
value: {
fontSize: "0.75rem",
lineHeight: "1rem",
},
fontSize: "0.75rem",
lineHeight: "1rem",
},
sm: {
value: {
fontSize: "0.875rem",
lineHeight: "1.25rem",
},
fontSize: "0.875rem",
lineHeight: "1.25rem",
},
md: {
value: {
fontSize: "1rem",
lineHeight: "1.5rem",
},
fontSize: "1rem",
lineHeight: "1.5rem",
},
lg: {
value: {
fontSize: "1.125rem",
lineHeight: "1.75rem",
},
fontSize: "1.125rem",
lineHeight: "1.75rem",
},
xl: {
value: {
fontSize: "1.25rem",
lineHeight: "1.75rem",
},
fontSize: "1.25rem",
lineHeight: "1.75rem",
},
"2xl": {
value: {
fontSize: "1.5rem",
lineHeight: "2rem",
},
fontSize: "1.5rem",
lineHeight: "2rem",
},
"3xl": {
value: {
fontSize: "1.875rem",
lineHeight: "2.25rem",
},
fontSize: "1.875rem",
lineHeight: "2.25rem",
},
"4xl": {
value: {
fontSize: "2.25rem",
lineHeight: "2.5rem",
},
fontSize: "2.25rem",
lineHeight: "2.5rem",
},
"5xl": {
value: {
fontSize: "3rem",
lineHeight: "1",
},
fontSize: "3rem",
lineHeight: "1",
},
"6xl": {
value: {
fontSize: "3.75rem",
lineHeight: "1",
},
fontSize: "3.75rem",
lineHeight: "1",
},
"7xl": {
value: {
fontSize: "4.5rem",
lineHeight: "1",
},
fontSize: "4.5rem",
lineHeight: "1",
},
"8xl": {
value: {
fontSize: "6rem",
lineHeight: "1",
},
fontSize: "6rem",
lineHeight: "1",
},
"9xl": {
value: {
fontSize: "8rem",
lineHeight: "1",
},
fontSize: "8rem",
lineHeight: "1",
},
};

export type TextStyle = keyof typeof textStyles;

export const fonts: Tokens["fonts"] = {
sans: { value: '"Spoqa Han Sans Neo", "Noto Sans KR", sans-serif' },
// TODO customize serif and mono font styles when needed
};

export const fontWeights: Tokens["fontWeights"] = {
thin: { value: "100" },
light: { value: "300" },
normal: { value: "400" },
medium: { value: "500" },
bold: { value: "700" },
export const fontWeights = {
thin: "100",
light: "300",
normal: "400",
medium: "500",
bold: "700",
};

export const fontSizes: Tokens["fontSizes"] = {
"2xs": { value: "0.5rem" },
xs: { value: "0.75rem" },
sm: { value: "0.875rem" },
md: { value: "1rem" },
lg: { value: "1.125rem" },
xl: { value: "1.25rem" },
"2xl": { value: "1.5rem" },
"3xl": { value: "1.875rem" },
"4xl": { value: "2.25rem" },
"5xl": { value: "3rem" },
"6xl": { value: "3.75rem" },
"7xl": { value: "4.5rem" },
"8xl": { value: "6rem" },
"9xl": { value: "8rem" },
export type FontWeight = keyof typeof fontWeights;

export const fontSizes = {
"2xs": "0.5rem",
xs: "0.75rem",
sm: "0.875rem",
md: "1rem",
lg: "1.125rem",
xl: "1.25rem",
"2xl": "1.5rem",
"3xl": "1.875rem",
"4xl": "2.25rem",
"5xl": "3rem",
"6xl": "3.75rem",
"7xl": "4.5rem",
"8xl": "6rem",
"9xl": "8rem",
};

export type FontSize = keyof typeof fontSizes;

export const letterSpacings: Tokens["letterSpacings"] = {
tighter: { value: "-0.05em" },
tight: { value: "-0.025em" },
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.app.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
"noFallthroughCasesInSwitch": true,
"verbatimModuleSyntax": true
},
"include": ["src", "styled-system"]
}
3 changes: 3 additions & 0 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ export default defineConfig({
environment: "happy-dom",
setupFiles: ["./src/setupTests.tsx"],
},
optimizeDeps: {
exclude: ["node_modules/.cache/storybook"],
},
});

0 comments on commit 676d8f8

Please sign in to comment.