diff --git a/.eslintrc b/.eslintrc index 7d72ee8c..c99fa561 100644 --- a/.eslintrc +++ b/.eslintrc @@ -85,6 +85,12 @@ "allowExpressions": true } ], + "react/no-unknown-property": [ + "error", + { + "ignore": ["css"] + } + ], "react/react-in-jsx-scope": "off", "react/require-default-props": [ "error", diff --git a/client/src/components/pages/MainPage.tsx b/client/src/components/pages/MainPage.tsx index ce3968cb..ed5dfd2f 100644 --- a/client/src/components/pages/MainPage.tsx +++ b/client/src/components/pages/MainPage.tsx @@ -1,7 +1,8 @@ import React, { useEffect } from "react"; -import { Box } from "@/components/atoms"; import { AgendaSection, ChatSection } from "@/components/organisms"; import { useAgenda } from "@/services/agenda"; +import { align, gap, justify, padding, row } from "@/styles"; +import { w, h } from "@/styles/size"; export const MainPage: React.FC = () => { const { retrieveAgendas } = useAgenda(state => ({ @@ -13,18 +14,20 @@ export const MainPage: React.FC = () => { }, []); return ( - - + ); }; diff --git a/client/src/styles/background.ts b/client/src/styles/background.ts new file mode 100644 index 00000000..27c1ad30 --- /dev/null +++ b/client/src/styles/background.ts @@ -0,0 +1,13 @@ +import { mapColors } from "@/styles/color"; +import { css } from "@emotion/react"; + +/** + * Applies background-color + * @example + * bg.gray100 // Apply gray100 background-color + */ +export const bg = mapColors( + color => css` + background-color: ${color}; + `, +); diff --git a/client/src/styles/border.ts b/client/src/styles/border.ts new file mode 100644 index 00000000..8056d591 --- /dev/null +++ b/client/src/styles/border.ts @@ -0,0 +1,14 @@ +import { css } from "@emotion/react"; +import { mapColors } from "@/styles/color"; + +/** + * Applies border + * @example + * border.gray300 // Apply gray300 border + * [border.gray300, round.md] // Usage with `round` mixin + */ +export const border = mapColors( + color => css` + border: 1px solid ${color}; + `, +); diff --git a/client/src/styles/color.ts b/client/src/styles/color.ts new file mode 100644 index 00000000..644899e8 --- /dev/null +++ b/client/src/styles/color.ts @@ -0,0 +1,32 @@ +export const colors = { + white: "#FFFFFF", + black: "#444444", + white100: "#FAFBFC", + gray100: "#FAFAFA", + gray200: "#EEEEEE", + gray300: "#D9D9D9", + gray400: "#B6B6B6", + gray500: "#8C8C8C", + gray600: "#555555", + grayTrans: "rgba(85, 85, 85, 0.1)", + blue100: "#FAFCFF", + blue200: "#E7F0FF", + blue300: "#BFDCFF", + blue400: "#6EABF4", + blue500: "#2F80DE", + blue600: "#065DAC", + blue700: "#004B81", + purpleLight: "#F6EEFE", + purple: "#9836EF", + orangeLight: "#FDF7E6", + orange: "#FF9211", + greenLight: "#E9F9EF", + green: "#008B1C", +} as const; + +type ColorKeys = keyof typeof colors; +export type Color = (typeof colors)[ColorKeys]; +export const mapColors = (cb: (color: Color) => T) => + Object.fromEntries( + Object.entries(colors).map(([key, value]) => [key, cb(value)] as const), + ) as Record; diff --git a/client/src/styles/index.ts b/client/src/styles/index.ts new file mode 100644 index 00000000..d17828a9 --- /dev/null +++ b/client/src/styles/index.ts @@ -0,0 +1,8 @@ +export * from "./background"; +export * from "./border"; +export * from "./color"; +export * from "./layout"; +export * from "./round"; +export * from "./spacing"; +export * from "./text"; +export * from "./size"; diff --git a/client/src/styles/layout.ts b/client/src/styles/layout.ts new file mode 100644 index 00000000..394ca47f --- /dev/null +++ b/client/src/styles/layout.ts @@ -0,0 +1,59 @@ +import { css } from "@emotion/react"; + +export const column = css` + display: flex; + flex-direction: column; +`; + +export const row = css` + display: flex; + flex-direction: row; +`; + +export const center = css` + display: flex; + align-items: center; + justify-content: center; +`; + +export const justify = { + start: css` + justify-content: flex-start; + `, + center: css` + justify-content: center; + `, + end: css` + justify-content: flex-end; + `, + between: css` + justify-content: space-between; + `, + around: css` + justify-content: space-around; + `, +} as const; + +export const align = { + start: css` + align-items: flex-start; + `, + center: css` + align-items: center; + `, + end: css` + align-items: flex-end; + `, + stretch: css` + align-items: stretch; + `, +} as const; + +/** + * Applies flexbox with column direction + * @example + * gap(10) // Apply 10px gap + */ +export const gap = (value: number) => css` + gap: ${value}px; +`; diff --git a/client/src/styles/round.ts b/client/src/styles/round.ts new file mode 100644 index 00000000..3faf1944 --- /dev/null +++ b/client/src/styles/round.ts @@ -0,0 +1,16 @@ +import { css } from "@emotion/react"; + +/** + * Applies border radius + * @example + * round.md // Apply 5px radius + * round.lg // Apply 10px radius + */ +export const round = { + md: css` + border-radius: 5px; + `, + lg: css` + border-radius: 10px; + `, +} as const; diff --git a/client/src/styles/size.ts b/client/src/styles/size.ts new file mode 100644 index 00000000..ea522c22 --- /dev/null +++ b/client/src/styles/size.ts @@ -0,0 +1,29 @@ +import { css } from "@emotion/react"; + +const applyAttribute = (attribute: "height" | "width") => + Object.assign( + (value: number) => css` + ${attribute}: ${value}px; + `, + { + fill: css` + ${attribute}: 100%; + `, + }, + ); + +/** + * Applies height + * @example + * h(10) // 10px height + * h.fill // 100% height + */ +export const h = applyAttribute("height"); + +/** + * Applies width + * @example + * w(10) // 10px width + * w.fill // 100% width + */ +export const w = applyAttribute("width"); diff --git a/client/src/styles/spacing.ts b/client/src/styles/spacing.ts new file mode 100644 index 00000000..83aed694 --- /dev/null +++ b/client/src/styles/spacing.ts @@ -0,0 +1,31 @@ +import { css } from "@emotion/react"; + +const style = (attribute: string) => (value: number) => css` + ${attribute}: ${value}px; +`; + +const applyPrefix = (prefix: "padding" | "margin") => + Object.assign(style(prefix), { + top: style(`${prefix}-top`), + bottom: style(`${prefix}-bottom`), + left: style(`${prefix}-left`), + right: style(`${prefix}-right`), + vertical: style(`${prefix}-block`), + horizontal: style(`${prefix}-inline`), + }); + +/** + * @example + * padding(10) // 10px padding for all sides + * padding.top(5) // 5px padding for top + * padding.horizontal(8) // 8px padding for left and right + */ +export const padding = applyPrefix("padding"); + +/** + * @example + * margin(10) // 10px margin for all sides + * margin.top(5) // 5px margin for top + * margin.horizontal(8) // 8px margin for left and right + */ +export const margin = applyPrefix("margin"); diff --git a/client/src/styles/text.ts b/client/src/styles/text.ts new file mode 100644 index 00000000..63e7854e --- /dev/null +++ b/client/src/styles/text.ts @@ -0,0 +1,32 @@ +import { css } from "@emotion/react"; +import { mapColors } from "@/styles/color"; + +const style = (size: number, weight: number) => css` + font-size: ${size}px; + font-weight: ${weight}; +`; + +/** + * Applies typography styles and text color + * @example + * text.title1 // Apply title1 typography + * text.gray300 // Apply gray300 text color + */ +export const text = { + ...mapColors( + color => css` + color: ${color}; + `, + ), + title1: style(15, 500), + title2: style(14, 500), + title3: style(13, 500), + subtitle: style(11, 500), + body: style(12, 500), + option1: style(10, 500), + option2: style(9, 500), + boldtitle1: style(14, 700), + boldtitle2: style(13, 700), + boldtitle3: style(12, 700), + boldtitle4: style(11, 700), +} as const; diff --git a/client/tsconfig.json b/client/tsconfig.json index c5d978e6..94e3fa96 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -21,6 +21,7 @@ "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", + "jsxImportSource": "@emotion/react", "types": ["vite-plugin-svgr/client"] }, "include": ["src", "vite.config.ts"], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 652749fe..c02c172d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2670,7 +2670,7 @@ packages: /eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} dependencies: - debug: 3.2.7 + debug: 3.2.7(supports-color@5.5.0) is-core-module: 2.13.0 resolve: 1.22.4 transitivePeerDependencies: @@ -2722,7 +2722,7 @@ packages: optional: true dependencies: '@typescript-eslint/parser': 6.2.1(eslint@8.48.0)(typescript@5.1.6) - debug: 3.2.7 + debug: 3.2.7(supports-color@5.5.0) eslint: 8.48.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.0(@typescript-eslint/parser@6.2.1)(eslint-plugin-import@2.28.1)(eslint@8.48.0) @@ -2745,7 +2745,7 @@ packages: array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.1 array.prototype.flatmap: 1.3.1 - debug: 3.2.7 + debug: 3.2.7(supports-color@5.5.0) doctrine: 2.1.0 eslint: 8.48.0 eslint-import-resolver-node: 0.3.9