์ฌ์ด๋ ํ๋ก์ ํธ ๋ค ๊ณณ์ ํ๋ฉด์ ํ๋ฃจ์ ์๋ดค๋ค. ์ถ๋ฐ์ ์ "๋์์ธ์ด ์ด์ค๋ฝ๋ค"์๋๋ฐ, ์ฝ๋๋ฅผ ์ด์ ์ด์ค๋ฌ์์ ๊ฒฐ๊ณผ์๊ณ ์์ธ์ ๋ฐ๋ก ์์๋ค.
๋ค ๊ณณ์ด ์ ๋ถ ๊ฐ์ ๋ณ์ด์๋ค. ์๊ณผ ํฌ๊ธฐ๋ฅผ ๊ฐ์ผ๋ก ์ง์ ์ฐ๋ค ๋ณด๋, ํ ๋ง๋ ํ๋ฉด ํฌ๊ธฐ๊ฐ ๋ฐ๋ ๋๋ง๋ค ์์ธ ๊ท์น์ ๋ง๋ถ์ด๋ ๊ตฌ์กฐ. ์ฆ์๋ง ํ๋ก์ ํธ๋ง๋ค ๋ค๋ฅด๊ฒ ๋ํ๋ฌ๋ค.
| ํ๋ก์ ํธ | ๊ฒ์ผ๋ก ๋๋ฌ๋ ์ฆ์ | ์ค์ ์์ธ | ์ ๋ฆฌ ํ |
|---|---|---|---|
| SEO ์ ์ ์๋ํฐ | ์ธ๋ผ์ธ style={{ }} 38๊ฐ |
์ ์์ ๋ฐ๋ผ ์์ด ๋ฐ๋๋ ๋์ ์คํ์ผ | 0๊ฐ |
| ํฌ๋ชจ๋๋ก ํ์ด๋จธ | ๋คํฌ ๋ชจ๋์์ ๊ธ์๊ฐ ๋ฌปํ | ๋คํฌ ํ ํฐ์ด 2๊ฐ๋ฟ | ๋ณด์ ์ ํ์ 19 โ 0 |
| ์ด ๋ธ๋ก๊ทธ | font-size์ ์์์ px |
html์ด 14px๋ก ๊ณ ์ ๋ผ rem์ด ์ด๊ธ๋จ |
๊ณ ์ ๊ฐ 34์ข โ 9๋จ๊ณ |
| ์ฌ์ดํธ๋งต ์์ฑ๊ธฐ | ๋ชจ๋ฐ์ผ์์ ๋ ์ด์์ ๋ถ๊ดด | @media ์ฟผ๋ฆฌ 0๊ฐ, ํ์ด์ง๋ง๋ค <head> ์ค๋ณต |
breakpoint 0 โ 4 |
๋ค ์ฆ์์ ํ๋์ฉ ๋ฐ๋ผ๊ฐ๋ฉด ๊ฐ์ ์๋ฆฌ์ ๋์ฐฉํ๋ค.
๋คํฌ ๋ชจ๋์์ ๊ธ์๊ฐ ๋ฐฐ๊ฒฝ์ ๋ฌปํ๋ค
๊ฐ์ฅ ์์๋ณด๊ธฐ ์ฌ์ด ์ฆ์๋ถํฐ ๋ณธ๋ค. ๋คํฌ ๋ชจ๋๋ฅผ ์ผฐ๋๋ฐ ์ผ๋ถ ๊ธ์๊ฐ ์ด๋์ด ๋ฐฐ๊ฒฝ์ ์ด๋์ด ์์ผ๋ก ๋จ์ ์ ๋ณด์ธ๋ค.
์์ธ์ ์ฝ๋ ์ฃผ์์ ์ด๋ฏธ ์ ํ ์์๋ค.
/* ๋คํฌ ๋ชจ๋ ๋๋น ๋ณด์ - text-color(#333) ๋ณ์๊ฐ ๋คํฌ์์ ์ ๋ฐ๋์ด
์ด๋์ด ๋ฐฐ๊ฒฝ์ ์ด๋์ด ๊ธ์จ๋ก ๋ฌปํ๋ ์์๋ค */
.App.dark-mode .task-buttons button {
color: var(--dark-text-color);
border-color: #444;
}์ฃผ์์ ์ ํํ๋ค. ๊ทธ๋ฐ๋ฐ ์ฒ๋ฐฉ์ด ๋ฐ๋ ๋ฐฉํฅ์ด์๋ค. ํ ํฐ ์ ์๋ฅผ ๋ณด๋ฉด ์๋ค.
:root {
--text-color: #333333;
--background-color: #ffffff;
--border-color: #dddddd;
--shadow-color: rgba(0, 0, 0, 0.1);
--dark-background-color: #121212; /* ๋คํฌ ์ ์ฉ์ ์ด ๋๋ฟ */
--dark-text-color: #ffffff;
}
body.dark-mode {
background-color: var(--dark-background-color);
color: var(--dark-text-color);
}๋ผ์ดํธ์๋ ํ ํฐ์ด ์ฌ๋ฌ ๊ฐ์ธ๋ฐ ๋คํฌ์๋ ๋ฐฐ๊ฒฝ๊ณผ ๊ธ์ ๋๋ฟ์ด๋ค. ํ ๋๋ฆฌ, ๊ทธ๋ฆผ์, ์นด๋ ํ๋ฉด, ์ ๋ ฅ ํ๋์ ํด๋นํ๋ ๋คํฌ ๊ฐ์ด ์๋ค. ๊ทธ๋ฌ๋ ๊ทธ ์์ด ํ์ํ ์๋ฆฌ๋ง๋ค ์ ํ์๋ฅผ ํ๋์ฉ ๋ง๋ค์ด ๋ฎ๋ ์๋ฐ์ ์๋ค.
๊ทธ๋ ๊ฒ ๋์ด๋ ๊ฒ์ด .App.dark-mode ๋ณด์ ์ ํ์ 19๊ฐ์๋ค.
.App.dark-mode .timer-container,
.App.dark-mode .task-list,
.App.dark-mode .modal-content,
.App.dark-mode .task-item {
background-color: #1e1e1e; /* ์ด๋ฆ ์๋ ๋คํฌ ํ๋ฉด์ */
color: var(--dark-text-color);
}
.App.dark-mode .task-text,
.App.dark-mode .task-input,
.App.dark-mode .task-input-edit {
color: var(--dark-text-color);
background-color: #2a2a2a; /* ์ด๋ฆ ์๋ ๋คํฌ ์
๋ ฅ ๋ฐฐ๊ฒฝ */
border-color: #444; /* ์ด๋ฆ ์๋ ๋คํฌ ํ
๋๋ฆฌ */
}์ฌ๊ธฐ์ ํ ๊ฐ์ง๊ฐ ๋ณด์ธ๋ค. ์์ธ ์ ํ์ ์์ ์ด๋ฏธ ํ ํฐ ๊ฐ์ด ๋ค์ด ์์๋ค. ์ด๋ฆ๋ง ์์์ ๋ฟ์ด๋ค. #1e1e1e๋ ์นด๋ ํ๋ฉด, #2a2a2a๋ ์
๋ ฅ ๋ฐฐ๊ฒฝ, #444๋ ํ
๋๋ฆฌ๋ค. ์
๋ค ์ฌ๋ฌ ์ ํ์์ ๋ฐ๋ณตํด์ ๋์จ๋ค.
์ด๋ฆ์ ๋ถ์ด๋ฉด ์์ธ๊ฐ ์ฌ๋ผ์ง๋ค.
:root {
--background-color: #ffffff; /* ํ์ด์ง ๋ฐ๋ฅ */
--surface-color: #ffffff; /* ์นด๋, ํจ๋, ๋ชจ๋ฌ */
--input-background: #ffffff; /* ์
๋ ฅ ํ๋ */
--text-color: #333333;
--border-color: #dddddd;
--hover-overlay: rgba(0, 0, 0, 0.05);
}
body.dark-mode {
--background-color: #121212;
--surface-color: #1e1e1e;
--input-background: #2a2a2a;
--text-color: #ffffff;
--border-color: #444444;
--hover-overlay: rgba(255, 255, 255, 0.08);
}ํต์ฌ์ ๋คํฌ๊ฐ ๋ณ๋ ํ ํฐ ์ด๋ฆ์ ๊ฐ์ง ์๋๋ค๋ ๊ฒ์ด๋ค. ๊ฐ์ ์ด๋ฆ ํ ๋ฒ์ ๋๊ณ body.dark-mode ์์์ ๊ฐ๋ง ๋ค์ ์ ์ํ๋ค. CSS ์ฌ์ฉ์ ์ ์ ์์ฑ์ ์์๋๋ฏ๋ก, --text-color๋ฅผ ์ฐ๋ ๋ชจ๋ ํ์ ์์๊ฐ ์๋์ผ๋ก ๋ฐ๋ผ์จ๋ค.mdn-custom-props
ํ ํฐ์ ๋ผ์ดํธ 19๊ฐ, ๋คํฌ ์ฌ์ ์ 9๊ฐ๋ก ๋๋ฆฌ๋ ๋ณด์ ์ ํ์๊ฐ ์ ๋ถ ์ฌ๋ผ์ก๋ค. ํ์ผ์ 945์ค์์ 902์ค์ด ๋๋ค.
์ธ๋ผ์ธ ์คํ์ผ 38๊ฐ๋ ๊ฒ์ผ๋ฆ์ด ์๋์๋ค
SEO ์ ์ ์๋ํฐ์๋ ์ธ๋ผ์ธ style={{ }}์ด 38๊ฐ ์์๋ค(๋ถ์ ํจ๋ 32๊ฐ, ์๋ํฐ 6๊ฐ). ์ฒ์์ ๊ธํ๊ฒ ์ง๋ค ๋ง ํ์ ์ผ๋ก ๋ณด์๋ค.
์ค์ ์ด์ ๋ ๋ฌ๋๋ค. ์ ์์ ๋ฐ๋ผ ์์ด ๋ฐ๋์ด์ผ ํ๊ณ , ์์ ๊ฐ์ผ๋ก๋ง ํํํ ์ค ์์๊ธฐ ๋๋ฌธ์ด๋ค.
// ํจ์๊ฐ ์๊ฐ์ ๋ฐํํ๋ค. ๊ทธ๋ฌ๋ ์ด ๊ฐ์ ๋ฐ์ ๊ณณ์ ์ธ๋ผ์ธ๋ฐ์ ์๋ค.
const getScoreColor = (score) => {
if (score >= 80) return "#22c55e";
if (score >= 60) return "#eab308";
if (score >= 40) return "#f97316";
return "#ef4444";
};
const color = getScoreColor(score);
<span style={{ color, fontWeight: "bold" }}>{check.passed ? "โ" : "โ"}</span>์๊ฐ์ด ์ปดํฌ๋ํธ ์์ ์์ผ๋ CSS ํ์ผ๋ก ์ฎ๊ธธ ๋ฐฉ๋ฒ์ด ์๋ค. ๊ทธ๋์ ์ธ๋ผ์ธ์ด ๋จ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ธ๋ผ์ธ์ด ํ๋ ํ์ฉ๋๋ฉด ๊ทธ ์์ ์ ์ ์คํ์ผ๊น์ง ๊ฐ์ด ์ธ๋ผ์ธ์ผ๋ก ์ ํ๊ธฐ ์์ํ๋ค. 38๊ฐ ์ค ์๋น์๋ textAlign, marginBottom ๊ฐ์ ๊ณ ์ ๊ฐ์ด์๋ค.
๊ณ ์น ๋ฐฉ๋ฒ์ ํจ์์ ๋ฐํ๊ฐ์ ์์ด ์๋๋ผ ํ ํฐ ์ด๋ฆ์ผ๋ก ๋ฐ๊พธ๋ ๊ฒ์ด๋ค.
/** ์ ์๋ฅผ semantic ํ ํฐ ์ด๋ฆ์ผ๋ก ์ฎ๊ธด๋ค. ์๊ฐ์ ์ปดํฌ๋ํธ์ ๋์ง ์๊ธฐ ์ํจ. */
const scoreTone = (score) => {
if (score >= 80) return "good";
if (score >= 60) return "fair";
if (score >= 40) return "warn";
return "poor";
};๋ฐํ ํ์
์ด "#22c55e"์์ "good"์ผ๋ก ๋ฐ๋ ๊ฒ๋ฟ์ธ๋ฐ, ์๊ฐ์ด ์ปดํฌ๋ํธ์์ ์์ ํ ๋น ์ง๋ค. ๋ฐ๋ ์ชฝ์ styled-components์ props ํจ์๋ก ํ ํฐ ์ด๋ฆ์ ์กฐ๋ฆฝํ๋ค.
const Stripe = styled.div`
height: 3px;
background: var(--${(p) => p.$tone});
`;
<Stripe $tone={scoreTone(score)} aria-hidden="true" />SVG์ฒ๋ผ styled๋ก ๊ฐ์ธ๊ธฐ ์ ๋งคํ ๊ณณ๋ ๊ฐ์ ๋ฌธ์์ด์ ๊ทธ๋๋ก ์ด๋ค.
<circle stroke={`var(--${tone})`} ... />ํ ํฐ ์ ์๋ ํ ๊ณณ์๋ง ๋๋ค. ์์คํ ํ ๋ง๋ฅผ ๋ฐ๋ฅด๋ ๋คํฌ ๋์๋ ์ฌ๊ธฐ์ ๋๋๋ค.mdn-prefers
:root {
--good: #15794a; --fair: #8a6300; --warn: #b8510c; --poor: #b52a30;
}
@media (prefers-color-scheme: dark) {
:root {
--good: #4ec98a; --fair: #dcb04a; --warn: #f0894a; --poor: #f27279;
}
}
$tone์ฒ๋ผ ๋ฌ๋ฌ ๊ธฐํธ๋ก ์์ํ๋ prop์ styled-components v5.1 ์ด์์์ transient prop์ผ๋ก ์ทจ๊ธ๋ผ ์ค์ DOM ์์๋ก ์ ๋ฌ๋์ง ์๋๋ค.sc-transient ์ด๊ฑธ ๋นผ๋จน์ผ๋ฉด<div tone="good">์ด ๊ทธ๋๋ก ๋ ๋๋ผ React๊ฐ ์ ์ ์๋ ์์ฑ ๊ฒฝ๊ณ ๋ฅผ ๋ธ๋ค.
๋ค์ผ๋ก ์ป์ ๊ฒ์ด ํ๋ ์๋ค. ์์ด ํ ํฐ ์ด๋ฆ์ผ๋ก ์ ๋ฆฌ๋์, ๊ฒ์ด์ง๋ฅผ ๋ณด์ง ์์๋ ์ํ๊ฐ ์ฝํ๋๋ก ๋ถ์ ํจ๋ ๋จธ๋ฆฌ์ ์ํ ๋ ๋ฅผ ๋๋ฅด๋ ์ผ์ด ์ธ ์ค๋ก ๋๋ฌ๋ค. ๊ฐ์ผ๋ก ํฉ์ด์ ธ ์์ ๋๋ ์๋๊ธฐ ์ด๋ ค์ ๋ ๋ณ๊ฒฝ์ด๋ค.
์์์ px์ ์ ์ฒด๋ ๋๋์ ๊ฒฐ๊ณผ์๋ค
์ด ๋ธ๋ก๊ทธ๋ ์ฆ์์ด ๋ ์ด์ํ๋ค. font-size๊ฐ 34์ข
์ด์๋๋ฐ ๊ทธ์ค์ 14.4px, 15.5px, 17.6px, 44.8px ๊ฐ์ ๊ฐ์ด ์์ฌ ์์๋ค.
์ ์ญ ์คํ์ผ์ ์์ธ์ด ์์๋ค.
html {
font-size: 14px;
}rem์ ๋ฃจํธ ์์์ ํฐํธ ํฌ๊ธฐ๋ฅผ ๊ธฐ์ค์ผ๋ก ํ๋ ์๋ ๋จ์๋ค.mdn-rem ๋ธ๋ผ์ฐ์ ๊ธฐ๋ณธ๊ฐ์ 16px์ธ๋ฐ ์ฌ๊ธฐ์ 14px๋ก ๊ณ ์ ํด ๋ฒ๋ ธ๋ค. ๊ทธ๋ฌ๋ฉด 0.9rem์ 14.4px์ด ์๋๋ผ 12.6px์ด ๋๋ค. 16px ๊ธฐ์ค์ผ๋ก ๊ณ์ฐํด ๋ ๋์์ธ ๊ฐ์ด ์ ๋ถ ์ด๊ธ๋๋ค.
๊ทธ๋์ rem์ ํฌ๊ธฐํ๊ณ px์ ์ง์ ์ ๋ ์ชฝ์ผ๋ก ํ๋ ๊ณ , ๊ทธ๋ ์ ํ ๊ฐ์ด 16 ร 0.9 = 14.4, 16 ร 2.8 = 44.8 ๊ฐ์ ๊ณ์ฐ ๊ฒฐ๊ณผ์๋ค. ์์์ px์ ์ค์๊ฐ ์๋๋ผ ์ฐํ์ ํ์ ์ด๋ค.
๋ ํฐ ๋ฌธ์ ๋ ๊ทธ ๊ฒฐ๊ณผ๋ก ์๊ธด ์๊ณ ๋ถ๊ดด์๋ค.
| ์ ๋ฆฌ ์ | ์ ๋ฆฌ ํ | |
|---|---|---|
| h2 | 22px | --fs-2xl |
| h3 | 16.5px | --fs-lg |
| h4 | 16px | --fs-md (bold) |
| ๋ณธ๋ฌธ | 15.5px | --fs-md |
h3, h4, ๋ณธ๋ฌธ์ด ์ฌ์ค์ ๊ฐ์ ํฌ๊ธฐ๋ค. ๊ตต๊ธฐ๋ง ๋ค๋ฅผ ๋ฟ ํฌ๊ธฐ๋ก๋ ๊ตฌ๋ถ๋์ง ์๋๋ค. ๊ธ ์์์ ๊ณ์ธต์ด ๋ณด์ด์ง ์๋ ์ด์ ๊ฐ ์ฌ๊ธฐ ์์๋ค.
9๋จ๊ณ ํ์
์ค์ผ์ผ์ ์ ์ํ๊ณ 27๊ฐ ํ์ผ 74๊ณณ์ ์นํํ๋ค. ์ปดํฌ๋ํธ์๋ var(--fs-*)๋ง ๋จ๊ธฐ๊ณ px์ ์ง์ ์ ์ง ์๋ ๊ท์น์ ์ฃผ์์ผ๋ก ๋ฐ์ ๋๋ค.
<title>Document</title>๊ฐ ๋ฐฐํฌ๋ผ ์์๋ค
์ฌ์ดํธ๋งต ์์ฑ๊ธฐ๋ @media ์ฟผ๋ฆฌ๊ฐ ํ๋๋ ์์ด ๋ชจ๋ฐ์ผ์์ 800px ๊ณ ์ ํญ์ด ๊ทธ๋๋ก ๋์๋ค. ํ๋๋ ๋ค ์ข
๋ฅ๊ฐ ๋์๋ค๋
๋ค(#0077b6, #007bff, #0056b3, #0d6efd. Bootstrap ๊ธฐ๋ณธ๊ฐ๊ณผ ์ง์ ๊ณ ๋ฅธ ๊ฐ์ด ๋ค์์ธ ๊ฒฐ๊ณผ๋ค).
๊ทผ๋ณธ ์์ธ์ ์์ด ์๋๋ผ ๊ตฌ์กฐ์๋ค. 5๊ฐ EJS ํ์ด์ง๊ฐ ๊ฐ์ <head>๋ฅผ ๊ด๋ฆฌํ๊ณ ์์๋ค. ํฐํธ ๋งํฌ๋ 5๊ฐ ์ค 1๊ฐ์๋ง ์์๊ณ , ์ ํ์ด์ง๋ฅผ ๋ง๋ค ๋๋ง๋ค ์ ํ์ด์ง๋ฅผ ๋ณต์ฌํด ์ค๋ค ๋ณด๋ ๋น ์ง ๊ฒ์ ์๋ฌด๋ ๋ชฐ๋๋ค.
views/partials/head.ejs ํ๋๋ก ๋ฌถ์๋ค. ๊ทธ ๊ณผ์ ์์ ์ด๊ฒ ๋์๋ค.
<!-- views/404.ejs -->
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>์๋ํฐ๊ฐ HTML ๊ณจ๊ฒฉ์ ์๋ ์์ฑํ ๋ ๋ฃ์ด ์ฃผ๋ ๊ธฐ๋ณธ ์ ๋ชฉ์ด๋ค. 404 ํ์ด์ง๋ผ ๊ฐ๋ฐ ์ค์ ์ด์ด ๋ณผ ์ผ์ด ์์๊ณ , ๊ทธ๋๋ก ๋ฐฐํฌ๋ผ ์์๋ค. ์ค๋ณต์ ์์ ๋ ์์ ์ ์ค๋ณต ๊ทธ ์์ฒด๋ณด๋ค ์ค๋ณต ๋ค์ ์จ์ด ์๋ ๊ฒ์ ๋๋ฌ๋ด๋ ์ชฝ์ด ๋ ๊ฐ์ง๋ค.
๊ฒ์ฆ์ ์ค์ ๋ ๋๋ก ํ๋ค. ์ด ํ๋ก์ ํธ๋ node_modules๊ฐ ์์ด์ EJS 5๊ฐ ํ์ด์ง๋ฅผ ์ง์ ๋ ๋ํด ํ์ธํ ์ ์์๋ค.
ํ ํฐํ๊ฐ ์ ํจ์ ์ ์ ์ด์๋ค
์ฌ๊ธฐ๊น์ง๊ฐ ์ ๋ฆฌ ๋น์ผ์ ์ด์ผ๊ธฐ๋ค. ๊ทธ๋ฐ๋ฐ ๋ค ๊ณณ ์ค ๋ ๊ณณ์์ 12์ผ, 18์ผ ๋ค์ ํ์ ์์ ์ปค๋ฐ์ด ๋๊ฐ๋ค. ํ ํฐํ๋ ๋์ด ์๋๋ผ ์ ์คํจ ๋ชจ๋์ ์์์ด์๋ค.
1. ์๊ธฐ ์์ ์ ์ฐธ์กฐํ CSS ๋ณ์๋ ์กฐ์ฉํ ๋ฌดํจ๊ฐ ๋๋ค
ํ ํฐ์ ์ฌ๋ฐฐ์นํ๋ค ์ด๋ฐ ์ค์ด ๋จ์๋ค.
:root {
--reset-color: var(--reset-color); /* ์๊ธฐ ์์ ์ ์ฐธ์กฐํ๋ค */
--danger-color: var(--danger-color);
}CSS๋ ์ค๋ฅ๋ฅผ ๋ด์ง ์๋๋ค. ๋น๋๋ ํต๊ณผํ๋ค. ์ฌ์์ ์ํ ์ฐธ์กฐ๊ฐ ์๋ ์ฌ์ฉ์ ์ ์ ์์ฑ์ computed-value ์์ ์ ๋ฌดํจ(invalid at computed-value time) ๋ก ์ฒ๋ฆฌ๋๊ณ , ๊ทธ ์์ฑ์ ์์๊ฐ์ด๋ ์ด๊ธฐ๊ฐ์ผ๋ก ๋จ์ด์ง๋ค.w3c-cycles ๊ฒฐ๊ณผ์ ์ผ๋ก ํด๋น ๋ฒํผ์ ์์ด ํต์งธ๋ก ์ฌ๋ผ์ง๋ค.
์๋ฌ ๋ฉ์์ง๊ฐ ์์ผ๋ ๊ฒ์ํ ๋ฌธ์์ด๋ ์๋ค. ํ๋ฉด์ ๋์ผ๋ก ๋ด์ผ๋ง ๋ฐ๊ฒฌ๋๋ค. ๊ฐ์ผ๋ก ๋๋๋ ค ๊ณ ์ณค๋ค.
--reset-color: #ff9800;
--danger-color: #f44336;2. ํ๋์ฝ๋ฉ์ ๊ฑท์ด๋ด๋ฉด ๋ธ๋ผ์ฐ์ ๊ธฐ๋ณธ๊ฐ์ด ๋๋ฌ๋๋ค
์์ธ ์ ํ์๋ฅผ ์ง์ฐ๋ฉด์ .App.dark-mode .task-input { background-color: #2a2a2a }๋ ๊ฐ์ด ์ง์ ๋ค. ๊ทธ๋ฐ๋ฐ .task-input ๋ณธ์ฒด์๋ ๋ฐฐ๊ฒฝ ์ง์ ์ด ์์ ์์๋ค. ์์ธ ์ ํ์๊ฐ ์ ์ผํ ์ง์ ์ด์๋ ๊ฒ์ด๋ค.
์ง์ฐ๊ณ ๋๋ ๋ธ๋ผ์ฐ์ ๊ธฐ๋ณธ ํฐ์์ด ๋๋ฌ๋, ๋คํฌ ๋ชจ๋์์ ์ ๋ ฅ์ฐฝ๋ง ํ์๊ฒ ํ์๋ค.
.task-input {
/* ์ง์ ํ์ง ์์ผ๋ฉด ๋ธ๋ผ์ฐ์ ๊ธฐ๋ณธ ํฐ์์ด๋ผ ๋คํฌ์์ ์
๋ ฅ์ฐฝ๋ง ํ๋ค */
background-color: var(--input-background);
color: var(--text-color);
}์์ธ๋ฅผ ์ง์ธ ๋๋ ๊ทธ ์์ธ๊ฐ ๋ฎ๊ณ ์๋ ๊ฒ์ด ๋ฌด์์ธ์ง๋ถํฐ ํ์ธํด์ผ ํ๋ค. ์์ธ๊ฐ ์ ์ผํ ์ง์ ์ด์๋ค๋ฉด ์ง์ฐ๋ ์๊ฐ ๊ธฐ๋ณธ๊ฐ์ด ์ฌ๋ผ์จ๋ค.
3. ํ ๋ง์ ๋ฌด๊ดํด์ผ ํ๋ ํ ํฐ์ด ๋ฐ๋ก ์๋ค
ํ ํฐ์ ์ฌ์ ์ํ๊ธฐ ์ง์ ์ ํ๊ท ํ๋๋ฅผ ๋ฐ๊ฒฌํ๋ค.
button {
color: var(--background-color); /* ์ปฌ๋ฌ ๋ฒํผ ์์ ํฐ ๊ธ์๋ฅผ ์๋ํ๋ค */
}์์ ์๋ --background-color๊ฐ ๋คํฌ์์ ์ ๋ฐ๋์๋ค. ๊ทธ๋์ ์ด ๊ธ์๋ ํญ์ ํฐ์์ด์๊ณ , ์ฐ์ฐํ ์๋๋๋ก ๋์ํ๋ค. ํ ํฐ์ ํ
๋ง๋ณ๋ก ์ฌ์ ์ํ๋ ์๊ฐ ์ด ๊ธ์๋ ๊ฒ์์์ด ๋๋ค. ์ด๋ก ๋ฒํผ ์์ ๊ฒ์ ๊ธ์จ๋ค.
์๋๋ "๋ฐฐ๊ฒฝ์"์ด ์๋๋ผ "์ปฌ๋ฌ ๋ฒํผ ์์ ์น๋ ๊ธ์์"์ด์๋ค. ์ด๋ฆ์ ์๋ก ๋ง๋ค์ด 3๊ณณ์ ์ฎ๊ฒผ๋ค.
--on-accent: #ffffff; /* ํ
๋ง์ ๋ฌด๊ด. ์ปฌ๋ฌ ๋ฒํผ ์์ ์น๋ ๊ธ์ */๊ฐ์ ํ ํฐ์ด ์ฌ์ดํธ๋งต ์์ฑ๊ธฐ์๋ ํ์ํ๋ค. ๋คํฌ ๋ชจ๋์์ ๋ฒํผ ๊ธ์๊ฐ WCAG AA ๋๋น ๊ธฐ์ค 4.5:1์ ๋ชป ๋๊ธฐ๊ณ ์์๋ค.wcag-contrast
ํ ๋ง๋ฅผ ๋ฐ๋ผ์ผ ํ๋ ํ ํฐ๊ณผ ๋ฐ๋ฅด๋ฉด ์ ๋๋ ํ ํฐ์ ๊ตฌ๋ถํ๋ ๊ฒ์ด ํ ๋ฒ ์ด์์ ์ ์ ๋ค. ํ๋ฉด๊ณผ ํ ์คํธ๋ ๋ฐ๋ผ์ผ ํ๊ณ , ๋ธ๋๋ ์๊ณผ ๊ทธ ์์ ์น๋ ๊ธ์์์ ๋ฐ๋ฅด๋ฉด ์ ๋๋ค.
์ ๋ฆฌ
- ์์ธ ๊ท์น์ ๊ฐ์๊ฐ ํ ํฐ ๋ถ์กฑ์ ์งํ๋ค.
.dark-mode๋ณด์ ์ ํ์ 19๊ฐ๋ ๋คํฌ ํ ํฐ์ด 2๊ฐ๋ผ๋ ์ฌ์ค์ ๋ค๋ฅธ ํํ์ด์๋ค - ์ธ๋ผ์ธ ์คํ์ผ์ด ์ ์์ด์ง๋ฉด ๋์ ์คํ์ผ์ ์์ฌํ๋ค. ํจ์๊ฐ ์๊ฐ์ ๋ฐํํ๋ ํ ๊ทธ ๊ฐ์ด ๊ฐ ๊ณณ์ ์ธ๋ผ์ธ๋ฟ์ด๋ค. ๋ฐํ๊ฐ์ ํ ํฐ ์ด๋ฆ์ผ๋ก ๋ฐ๊พธ๋ฉด ํ๋ฆฐ๋ค
- ์์์ px์ ์ด๋๊ฐ์์ ๋๋์
์ ํ๋ค๋ ๋ป์ด๋ค.
html { font-size }๋ฅผ ๊ฑด๋๋ ธ๋์ง ๋จผ์ ๋ณธ๋ค - ์์ธ ์ ํ์ ์์ ์ด๋ฏธ ํ ํฐ ๊ฐ์ด ์๋ค. ๋ฐ๋ณต๋๋ ํ๋์ฝ๋ฉ ์์ ์ธ์ด ๋ณด๋ฉด ํ์ํ ํ ํฐ ๋ชฉ๋ก์ด ๊ทธ๋๋ก ๋์จ๋ค
- ํ ํฐํ ์งํ๊ฐ ๊ฐ์ฅ ์ํํ๋ค. ์์ธ๋ฅผ ์ง์ฐ๋ฉด ๊ทธ ์์ธ๊ฐ ์ ์ผํ ์ง์ ์ด์๋ ์๋ฆฌ์์ ๊ธฐ๋ณธ๊ฐ์ด ์ฌ๋ผ์จ๋ค
๊ฐ์ฅ ์ค์ํ ํ๋๋ฅผ ๊ณ ๋ฅด๋ผ๋ฉด ์ฒซ ๋ฒ์งธ๋ค. ์์ธ๋ ๊ฒ์ผ๋ฆ์ ๊ฒฐ๊ณผ๊ฐ ์๋๋ผ ์ดํ๊ฐ ๋ชจ์๋ ๊ณณ์์ ์๊ธฐ๋ ์ฐํ๋ก๋ค. ์ฐํ๋ก๋ฅผ ํ๋์ฉ ๋ง๋ ๋์ ์ดํ๋ฅผ ๋๋ฆฌ๋ฉด ์ฐํ๋ก๊ฐ ์ค์ค๋ก ์ฌ๋ผ์ง๋ค.
-
.dark-mode,.theme-*๊ฐ์ ๋ณด์ ์ ํ์์ ๊ฐ์๋ฅผ ์ผ๋ค. ๋ค์ฏ ๊ฐ๋ฅผ ๋์ผ๋ฉด ํ ํฐ์ด ๋ถ์กฑํ ๊ฒ์ด๋ค - ๋คํฌ ์ ์ฉ ํ ํฐ ์ด๋ฆ(
--dark-*)์ ๋ง๋ค์ง ์๋๋ค. ๊ฐ์ ์ด๋ฆ ํ ๋ฒ์ ํ ๋ง ๋ธ๋ก์์ ์ฌ์ ์ํ๋ค - ์์ธ ์ ํ์์ ๋ฐ๋ณตํด ๋์ค๋ ํ๋์ฝ๋ฉ ์์ ๋ฝ์ ์ด๋ฆ์ ๋ถ์ธ๋ค. ๊ทธ๊ฒ ๋น ์ง ํ ํฐ ๋ชฉ๋ก์ด๋ค
- ์๊ฐ์ ๋ฐํํ๋ ํจ์๋ฅผ ์ฐพ์ ํ ํฐ ์ด๋ฆ์ ๋ฐํํ๋๋ก ๋ฐ๊พผ๋ค
- styled-components ๋์ prop์๋
$์ ๋์ฌ๋ฅผ ๋ถ์ธ๋ค -
html { font-size }๊ฐ 16px์ด ์๋์ง ํ์ธํ๋ค. ์์์ px์ ์ถ์ฒ๋ค - ์ ๋ชฉ ๋จ๊ณ๋ณ ํฌ๊ธฐ๊ฐ ์ค์ ๋ก ๋ค๋ฅธ์ง ํ์ธํ๋ค. h3, h4, ๋ณธ๋ฌธ์ด ๊ฐ์ ๊ฐ์ด๋ฉด ์๊ณ๊ฐ ์๋ ๊ฒ์ด๋ค
- ํ ํฐ ์ ์์
--x: var(--x)์๊ธฐ์ฐธ์กฐ๊ฐ ์๋์ง ํ๋๋ค. ๋น๋๋ ํต๊ณผํ๊ณ ์๋ง ์ฌ๋ผ์ง๋ค - ์์ธ ์ ํ์๋ฅผ ์ง์ฐ๊ธฐ ์ ์, ๊ทธ ์์ฑ์ด ๋ณธ์ฒด์๋ ์ง์ ๋ผ ์๋์ง ํ์ธํ๋ค
- ๋ธ๋๋ ์ ์์ ์น๋ ๊ธ์์์ ํ
๋ง ๋ฌด๊ด ํ ํฐ(
--on-accent)์ผ๋ก ๋ถ๋ฆฌํ๋ค - ์ฌ๋ฌ ํ์ด์ง๊ฐ ๊ฐ์
<head>๋ฅผ ๊ด๋ฆฌํ๊ณ ์์ผ๋ฉด partial๋ก ๋ฌถ๋๋ค. ๋น ์ง ๋ฉํ ํ๊ทธ๊ฐ ๋๋ฌ๋๋ค
๋ค์ ๊ธ์์๋ ๊ฒ์ ์ชฝ์ผ๋ก ๊ฐ๋ค. ํ๊ตญ์ด ๋ฌธ์๋ฅผ SQLite FTS5๋ก ๊ฒ์ํ ๋ ๊ธฐ๋ณธ ํ ํฌ๋์ด์ ๊ฐ ๋ ๊ธ์ ๋จ์ด๋ฅผ ๋ชป ์ก๋ ์ด์ ์, ํค์๋์ ์๋ฏธ ๊ฒ์์ ์์๋ก๋ง ํฉ์น๋ฉด ์ ํํ ์ผ์นํ ๊ฒฐ๊ณผ๊ฐ ์คํ๋ ค ๋ฐ๋ ค๋๋ ๊ตฌ์กฐ๋ฅผ ๋ค๋ฃฌ๋ค.
์ฐธ๊ณ ๋ฌธํ
์ด์ด์ ์ฝ๊ธฐ
- CSS Flexbox & Grid ์์ ์ ๋ณต: ์ธ์ ๋ญ ์จ์ผ ํ๋๊ฐ
- ์น ์ ๊ทผ์ฑ(Web Accessibility) ์์ ๊ฐ์ด๋: WCAG ๊ธฐ์ค๊ณผ ์ค์ ๊ตฌํ ๋ฐฉ๋ฒ
- rpm -Vac๋ก ๋ฐ๋ ์ค์ ๋ง ๋ฝ๊ธฐ: ๊ฒฐ๊ณผ 0๊ฑด์ด cannot open Packages database์ผ ๋
- MDN Web Docs. "Using CSS custom properties (variables)" (์ฌ์ฉ์ ์ ์ ์์ฑ์ ์์๊ณผ ์ฌ์ ์). https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascading_variables/Using_CSS_custom_propertiesโฉ
- MDN Web Docs. "prefers-color-scheme" (์์คํ ํ ๋ง ์ง์). https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-schemeโฉ
- styled-components. "Transient props" (
$์ ๋์ฌ prop์ DOM์ผ๋ก ์ ๋ฌ๋์ง ์๋๋ค). https://styled-components.com/docs/api#transient-propsโฉ - MDN Web Docs. "CSS length: rem" (๋ฃจํธ ์์ ๊ธฐ์ค ์๋ ๋จ์). https://developer.mozilla.org/en-US/docs/Web/CSS/length#remโฉ
- W3C. "CSS Custom Properties for Cascading Variables Module Level 1 - Resolving Dependency Cycles" (์ํ ์ฐธ์กฐ๋ invalid at computed-value time). https://www.w3.org/TR/css-variables-1/#cyclesโฉ
- W3C. "WCAG 2.1 Success Criterion 1.4.3 Contrast (Minimum)" (๋ณธ๋ฌธ ๋๋น 4.5:1). https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.htmlโฉ




