04더한 줄과 뺀 줄을 나눠 보는 diff
더한 줄은 초록 띠, 뺀 줄은 붉은 띠를 왼쪽에 두르고 기호 칸은 별도 span 이라 본문 코드만 복사됩니다. 바뀐 데가 없는 구간은 한 줄로 접혀 있다가 눌러야 펴집니다.
쓰임설정 파일 한 줄만 바꾸라고 안내하는 마이그레이션 문서
aria-expandedborder-inline-startsteps(1, end)

<main class="stage">
<div class="cd is-demo" id="cd">
<div class="cd__pane">
<div class="cd__head">
<span class="cd__ic" aria-hidden="true"><svg viewBox="0 0 256 256" width="14" height="14"><g fill="currentColor"><path d="M80 64a24 24 0 1 1-24-24a24 24 0 0 1 24 24m120 104a24 24 0 1 0 24 24a24 24 0 0 0-24-24" opacity=".2"/><path d="M112 152a8 8 0 0 0-8 8v28.69L66.34 151a8 8 0 0 1-2.34-5.63V95a32 32 0 1 0-16 0v50.38a23.85 23.85 0 0 0 7 17L92.69 200H64a8 8 0 0 0 0 16h48a8 8 0 0 0 8-8v-48a8 8 0 0 0-8-8M40 64a16 16 0 1 1 16 16a16 16 0 0 1-16-16m168 97v-50.37a23.85 23.85 0 0 0-7-17L163.31 56H192a8 8 0 0 0 0-16h-48a8 8 0 0 0-8 8v48a8 8 0 0 0 16 0V67.31L189.66 105a8 8 0 0 1 2.34 5.66V161a32 32 0 1 0 16 0m-8 47a16 16 0 1 1 16-16a16 16 0 0 1-16 16"/></g></svg></span>
<span class="cd__file">config.css</span>
</div>
<span class="cd__cursor" aria-hidden="true"></span>
<div class="cd__body">
<div class="cd__line cd__line--top">
<span class="cd__sign" aria-hidden="true"> </span><span class="cd__src"><span class="k">.card</span> <span class="p">{</span></span>
</div>
<button class="cd__fold" id="cd-fold" type="button" aria-expanded="false" aria-controls="cd-hidden" aria-label="가려진 줄 펼치기">
<span class="cd__sign" aria-hidden="true"> </span><span class="cd__src cd__dots">···</span>
</button>
<div class="cd__fold-body" id="cd-hidden">
<div class="cd__fold-in">
<div class="cd__line">
<span class="cd__sign" aria-hidden="true"> </span><span class="cd__src"> <span class="k">display</span><span class="p">:</span> <span class="s">flex</span><span class="p">;</span></span>
</div>
<div class="cd__line">
<span class="cd__sign" aria-hidden="true"> </span><span class="cd__src"> <span class="k">align-items</span><span class="p">:</span> <span class="s">center</span><span class="p">;</span></span>
</div>
</div>
</div>
<div class="cd__line cd__line--del cd__line--del1">
<span class="cd__sign">-</span><span class="cd__src"> <span class="k">padding</span><span class="p">:</span> <span class="n">12px 16px</span><span class="p">;</span></span>
</div>
<div class="cd__line cd__line--del cd__line--del2">
<span class="cd__sign">-</span><span class="cd__src"> <span class="k">border-radius</span><span class="p">:</span> <span class="n">4px</span><span class="p">;</span></span>
</div>
<div class="cd__line cd__line--add cd__line--add1">
<span class="cd__sign">+</span><span class="cd__src"> <span class="k">padding</span><span class="p">:</span> <span class="n">16px 20px</span><span class="p">;</span></span>
</div>
<div class="cd__line cd__line--add cd__line--add2">
<span class="cd__sign">+</span><span class="cd__src"> <span class="k">border-radius</span><span class="p">:</span> <span class="n">12px</span><span class="p">;</span></span>
</div>
<div class="cd__line cd__line--top">
<span class="cd__sign" aria-hidden="true"> </span><span class="cd__src"><span class="p">}</span></span>
</div>
</div>
</div>
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-base;
$easing: $ease-spring;
$color: $subject-mint;
@include stage($stage-paper);
// 구문 색 — 어두운 패널
$syn-key: $stage-orange;
$syn-str: $stage-yellow;
$syn-num: $subject-sky;
$syn-dim: rgba($subject-cream, .72);
$syn-txt: $subject-cream;
.cd {
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
.cd__pane {
position: relative;
box-sizing: border-box;
width: 288px;
border-radius: $r-card;
overflow: hidden;
box-shadow: $shadow-raised;
background: $stage-ink;
color: $syn-txt;
}
// 포인터가 접힘 줄 쪽으로 다가갔다가 돌아오는 작은 신호 — 실제 커서를 그리지 않고
// 옅은 점 하나로 시선의 이동만 암시한다 (면적은 작아 대표 프레임 선정에 영향 없음)
.cd__cursor {
position: absolute;
inset-inline-end: $sp-2;
top: 34px;
width: 6px;
height: 6px;
border-radius: 50%;
background: $subject-sky;
box-shadow: 0 0 0 4px rgba($subject-sky, .18);
opacity: 0;
pointer-events: none;
}
.cd__head {
display: flex;
align-items: center;
gap: $sp-2;
height: 28px;
padding: 0 $sp-3;
background: rgba(255, 247, 230, .08);
}
.cd__ic { display: inline-flex; flex: 0 0 auto; color: $subject-sky; }
.cd__ic svg { display: block; }
.cd__file {
font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
font-size: 11px;
line-height: 16px;
font-weight: 700;
color: $syn-txt;
}
.cd__body {
padding: $sp-3;
font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
font-size: 11px;
line-height: 18px;
}
.cd__line {
display: flex;
align-items: flex-start;
}
.cd__sign {
flex: 0 0 auto;
width: 12px;
color: $syn-dim;
user-select: none;
}
.cd__src { flex: 1 1 auto; min-width: 0; white-space: pre; }
.cd__src .k { color: $syn-key; }
.cd__src .s { color: $syn-str; }
.cd__src .n { color: $syn-num; }
.cd__src .p { color: $syn-dim; }
// 더한 줄 · 뺀 줄 — 왼쪽 띠 + 그 색의 옅은 배경
.cd__line--add {
border-inline-start: 2px solid $color;
background: rgba($color, .16);
}
.cd__line--add .cd__sign { color: $color; font-weight: 700; }
.cd__line--del {
border-inline-start: 2px solid $error;
background: rgba($error, .18);
}
.cd__line--del .cd__sign { color: $error-soft; font-weight: 700; }
// 옅은 민트·붉은 배경이 깔린 줄은 무대(먹)보다 밝다 — 그 위의 주황 키워드는 4.02:1 로 모자란다.
// ⑤(어두운 면 위 파란 글자 → $subject-blue-light)와 같은 방식으로, 토큰을 섞어 한 단계 밝힌 주황을 쓴다.
.cd__line--add .cd__src .k,
.cd__line--del .cd__src .k { color: mix($stage-orange, $subject-cream, 70%); }
.cd__line--top .cd__src { color: $syn-dim; }
// 왼쪽 띠 자체가 자라는 연출용 레이어 — 실제 테두리 위에 덧대는 게 아니라
// 테두리는 늘 실재(정적 상태에서도 보인다)하고, 이 안쪽 살짝 밝은 켜짐만 스케일로 차오른다
.cd__line--add,
.cd__line--del { position: relative; }
.cd__line--add::before,
.cd__line--del::before {
content: "";
position: absolute;
inset-inline-start: -2px;
inset-block: 0;
width: 2px;
transform: scaleY(1);
transform-origin: top center; // 규칙8: diff 띠 위에서 차오름
}
.cd__line--add::before { background: $color; }
.cd__line--del::before { background: $error; }
// ── 접힌 중간 줄 ───────────────────────────────────────
.cd__fold {
display: flex;
align-items: flex-start;
width: 100%;
margin: 0;
padding: 0;
border: 0;
background: none;
color: inherit;
font: inherit;
font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
font-size: 11px;
line-height: 18px;
cursor: pointer;
}
.cd__fold:focus-visible { outline: 2px solid $subject-sky; outline-offset: 1px; }
.cd__dots { color: $syn-dim; letter-spacing: 2px; }
.cd__fold-body {
display: grid;
grid-template-rows: 0fr;
overflow: hidden;
transition: grid-template-rows $duration $easing;
}
.cd.is-open .cd__fold-body { grid-template-rows: 1fr; }
.cd__fold-in { min-height: 0; }
// ── 자동 루프 (2s) ─────────────────────────────────────
// 0~14% 정지 · 16%(≈14~30%) 접힘 줄이 부드럽게 펴짐(0fr→1fr) · 20~40% 추가/삭제 띠가 시차를 두고
// 차오름 · 44~72% 정지 · 82~86% 다시 접힘(빠른 컷) · 84~100% 정지. 0%=100% 상태가 같아 이음매가 없다
.cd.is-demo .cd__fold-body {
animation-name: cd-fold;
animation-duration: $dur-loop;
animation-timing-function: $easing;
animation-iteration-count: infinite;
}
@keyframes cd-fold {
0% { grid-template-rows: 0fr; }
14% { grid-template-rows: 0fr; animation-timing-function: $easing; }
30% { grid-template-rows: 1fr; }
82% { grid-template-rows: 1fr; animation-timing-function: steps(1, end); }
86% { grid-template-rows: 0fr; }
100% { grid-template-rows: 0fr; }
}
.cd.is-demo .cd__dots {
animation-name: cd-dots;
animation-duration: $dur-loop;
animation-timing-function: steps(1, end);
animation-iteration-count: infinite;
}
@keyframes cd-dots {
0% { opacity: 1; }
30% { opacity: 0; }
86% { opacity: 1; }
100% { opacity: 1; }
}
// 추가·삭제 줄의 띠 — 지연을 다르게 줘 차례로 차오르게 한다. 지연 동안은 backwards 로 0 을 유지해
// 0프레임에 완성 상태가 번쩍이지 않게 한다 (함정 6)
.cd.is-demo .cd__line--del1::before,
.cd.is-demo .cd__line--del2::before,
.cd.is-demo .cd__line--add1::before,
.cd.is-demo .cd__line--add2::before {
animation-name: cd-bar;
animation-duration: $dur-loop;
animation-timing-function: $easing;
animation-iteration-count: infinite;
animation-fill-mode: backwards;
}
.cd.is-demo .cd__line--del1::before { animation-delay: 0ms; }
.cd.is-demo .cd__line--del2::before { animation-delay: 70ms; }
.cd.is-demo .cd__line--add1::before { animation-delay: 140ms; }
.cd.is-demo .cd__line--add2::before { animation-delay: 210ms; }
@keyframes cd-bar {
0% { transform: scaleY(0); }
16% { transform: scaleY(0); }
30% { transform: scaleY(1); }
100% { transform: scaleY(1); }
}
.cd.is-demo .cd__cursor {
animation-name: cd-cursor;
animation-duration: $dur-loop;
animation-timing-function: $easing;
animation-iteration-count: infinite;
}
@keyframes cd-cursor {
0% { opacity: 0; transform: translateY(0); }
6% { opacity: .9; transform: translateY(0); }
14% { opacity: .9; transform: translateY(54px); animation-timing-function: steps(1, end); }
18% { opacity: 0; transform: translateY(54px); }
44% { opacity: 0; transform: translateY(54px); }
50% { opacity: .9; transform: translateY(54px); }
72% { opacity: .9; transform: translateY(0); }
80% { opacity: 0; transform: translateY(0); }
100% { opacity: 0; transform: translateY(0); }
}
@media (max-width: 340px) {
.cd__pane { width: 100%; }
.cd__body,
.cd__fold { font-size: 10px; line-height: 16px; padding: $sp-2; }
.cd__body { padding: $sp-2; }
}
@media (prefers-reduced-motion: reduce) {
.cd.is-demo .cd__fold-body,
.cd.is-demo .cd__dots,
.cd.is-demo .cd__cursor,
.cd.is-demo .cd__line--del1::before,
.cd.is-demo .cd__line--del2::before,
.cd.is-demo .cd__line--add1::before,
.cd.is-demo .cd__line--add2::before { animation: none !important; }
.cd.is-demo .cd__cursor { opacity: 0; }
.cd.is-demo .cd__line--del1::before,
.cd.is-demo .cd__line--del2::before,
.cd.is-demo .cd__line--add1::before,
.cd.is-demo .cd__line--add2::before { transform: scaleY(1); }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ffffff;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.cd {
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
.cd__pane {
position: relative;
box-sizing: border-box;
width: 288px;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
background: #17141a;
color: #fff7e6;
}
.cd__cursor {
position: absolute;
inset-inline-end: 8px;
top: 34px;
width: 6px;
height: 6px;
border-radius: 50%;
background: #6ec8ff;
box-shadow: 0 0 0 4px rgba(110, 200, 255, 0.18);
opacity: 0;
pointer-events: none;
}
.cd__head {
display: flex;
align-items: center;
gap: 8px;
height: 28px;
padding: 0 12px;
background: rgba(255, 247, 230, 0.08);
}
.cd__ic {
display: inline-flex;
flex: 0 0 auto;
color: #6ec8ff;
}
.cd__ic svg {
display: block;
}
.cd__file {
font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
font-size: 11px;
line-height: 16px;
font-weight: 700;
color: #fff7e6;
}
.cd__body {
padding: 12px;
font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
font-size: 11px;
line-height: 18px;
}
.cd__line {
display: flex;
align-items: flex-start;
}
.cd__sign {
flex: 0 0 auto;
width: 12px;
color: rgba(255, 247, 230, 0.72);
user-select: none;
}
.cd__src {
flex: 1 1 auto;
min-width: 0;
white-space: pre;
}
.cd__src .k {
color: #ff4d1f;
}
.cd__src .s {
color: #ffd23f;
}
.cd__src .n {
color: #6ec8ff;
}
.cd__src .p {
color: rgba(255, 247, 230, 0.72);
}
.cd__line--add {
border-inline-start: 2px solid #4cd4a6;
background: rgba(76, 212, 166, 0.16);
}
.cd__line--add .cd__sign {
color: #4cd4a6;
font-weight: 700;
}
.cd__line--del {
border-inline-start: 2px solid #c2361a;
background: rgba(194, 54, 26, 0.18);
}
.cd__line--del .cd__sign {
color: #ffe0d8;
font-weight: 700;
}
.cd__line--add .cd__src .k,
.cd__line--del .cd__src .k {
color: rgb(100%, 50.1960784314%, 35.568627451%);
}
.cd__line--top .cd__src {
color: rgba(255, 247, 230, 0.72);
}
.cd__line--add,
.cd__line--del {
position: relative;
}
.cd__line--add::before,
.cd__line--del::before {
content: "";
position: absolute;
inset-inline-start: -2px;
inset-block: 0;
width: 2px;
transform: scaleY(1);
transform-origin: top center;
}
.cd__line--add::before {
background: #4cd4a6;
}
.cd__line--del::before {
background: #c2361a;
}
.cd__fold {
display: flex;
align-items: flex-start;
width: 100%;
margin: 0;
padding: 0;
border: 0;
background: none;
color: inherit;
font: inherit;
font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
font-size: 11px;
line-height: 18px;
cursor: pointer;
}
.cd__fold:focus-visible {
outline: 2px solid #6ec8ff;
outline-offset: 1px;
}
.cd__dots {
color: rgba(255, 247, 230, 0.72);
letter-spacing: 2px;
}
.cd__fold-body {
display: grid;
grid-template-rows: 0fr;
overflow: hidden;
transition: grid-template-rows 300ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.cd.is-open .cd__fold-body {
grid-template-rows: 1fr;
}
.cd__fold-in {
min-height: 0;
}
.cd.is-demo .cd__fold-body {
animation-name: cd-fold;
animation-duration: 2s;
animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
animation-iteration-count: infinite;
}
@keyframes cd-fold {
0% {
grid-template-rows: 0fr;
}
14% {
grid-template-rows: 0fr;
animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
}
30% {
grid-template-rows: 1fr;
}
82% {
grid-template-rows: 1fr;
animation-timing-function: steps(1, end);
}
86% {
grid-template-rows: 0fr;
}
100% {
grid-template-rows: 0fr;
}
}
.cd.is-demo .cd__dots {
animation-name: cd-dots;
animation-duration: 2s;
animation-timing-function: steps(1, end);
animation-iteration-count: infinite;
}
@keyframes cd-dots {
0% {
opacity: 1;
}
30% {
opacity: 0;
}
86% {
opacity: 1;
}
100% {
opacity: 1;
}
}
.cd.is-demo .cd__line--del1::before,
.cd.is-demo .cd__line--del2::before,
.cd.is-demo .cd__line--add1::before,
.cd.is-demo .cd__line--add2::before {
animation-name: cd-bar;
animation-duration: 2s;
animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
animation-iteration-count: infinite;
animation-fill-mode: backwards;
}
.cd.is-demo .cd__line--del1::before {
animation-delay: 0ms;
}
.cd.is-demo .cd__line--del2::before {
animation-delay: 70ms;
}
.cd.is-demo .cd__line--add1::before {
animation-delay: 140ms;
}
.cd.is-demo .cd__line--add2::before {
animation-delay: 210ms;
}
@keyframes cd-bar {
0% {
transform: scaleY(0);
}
16% {
transform: scaleY(0);
}
30% {
transform: scaleY(1);
}
100% {
transform: scaleY(1);
}
}
.cd.is-demo .cd__cursor {
animation-name: cd-cursor;
animation-duration: 2s;
animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
animation-iteration-count: infinite;
}
@keyframes cd-cursor {
0% {
opacity: 0;
transform: translateY(0);
}
6% {
opacity: 0.9;
transform: translateY(0);
}
14% {
opacity: 0.9;
transform: translateY(54px);
animation-timing-function: steps(1, end);
}
18% {
opacity: 0;
transform: translateY(54px);
}
44% {
opacity: 0;
transform: translateY(54px);
}
50% {
opacity: 0.9;
transform: translateY(54px);
}
72% {
opacity: 0.9;
transform: translateY(0);
}
80% {
opacity: 0;
transform: translateY(0);
}
100% {
opacity: 0;
transform: translateY(0);
}
}
@media (max-width: 340px) {
.cd__pane {
width: 100%;
}
.cd__body,
.cd__fold {
font-size: 10px;
line-height: 16px;
padding: 8px;
}
.cd__body {
padding: 8px;
}
}
@media (prefers-reduced-motion: reduce) {
.cd.is-demo .cd__fold-body,
.cd.is-demo .cd__dots,
.cd.is-demo .cd__cursor,
.cd.is-demo .cd__line--del1::before,
.cd.is-demo .cd__line--del2::before,
.cd.is-demo .cd__line--add1::before,
.cd.is-demo .cd__line--add2::before {
animation: none !important;
}
.cd.is-demo .cd__cursor {
opacity: 0;
}
.cd.is-demo .cd__line--del1::before,
.cd.is-demo .cd__line--del2::before,
.cd.is-demo .cd__line--add1::before,
.cd.is-demo .cd__line--add2::before {
transform: scaleY(1);
}
}// 실제 동작: 접힌 중간 줄을 펼치고 접는다. 사람이 누르면 자동 루프를 멈춘다
var cd = document.getElementById('cd');
var fold = document.getElementById('cd-fold');
fold.addEventListener('click', function () {
cd.classList.remove('is-demo');
var open = fold.getAttribute('aria-expanded') === 'true';
fold.setAttribute('aria-expanded', String(!open));
fold.setAttribute('aria-label', open ? '가려진 줄 펼치기' : '가려진 줄 접기');
cd.classList.toggle('is-open', !open);
});import { useState } from "react";
import type { CSSProperties } from "react";
import styles from "./DiffAddedRemoved.module.scss";
export type DiffAddedRemovedProps = {
fileName?: string;
openLabel?: string;
closeLabel?: string;
/** 자동 재생 루프 (갤러리용). 사람이 손대면 꺼진다 */
demo?: boolean;
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
function DiffIcon() {
return (
<svg viewBox="0 0 256 256" width="14" height="14" aria-hidden="true">
<g fill="currentColor">
<path
d="M80 64a24 24 0 1 1-24-24a24 24 0 0 1 24 24m120 104a24 24 0 1 0 24 24a24 24 0 0 0-24-24"
opacity=".2"
/>
<path d="M112 152a8 8 0 0 0-8 8v28.69L66.34 151a8 8 0 0 1-2.34-5.63V95a32 32 0 1 0-16 0v50.38a23.85 23.85 0 0 0 7 17L92.69 200H64a8 8 0 0 0 0 16h48a8 8 0 0 0 8-8v-48a8 8 0 0 0-8-8M40 64a16 16 0 1 1 16 16a16 16 0 0 1-16-16m168 97v-50.37a23.85 23.85 0 0 0-7-17L163.31 56H192a8 8 0 0 0 0-16h-48a8 8 0 0 0-8 8v48a8 8 0 0 0 16 0V67.31L189.66 105a8 8 0 0 1 2.34 5.66V161a32 32 0 1 0 16 0m-8 47a16 16 0 1 1 16-16a16 16 0 0 1-16 16" />
</g>
</svg>
);
}
export default function DiffAddedRemoved({
fileName = "config.css",
openLabel = "Show hidden lines",
closeLabel = "Hide expanded lines",
demo = true,
duration = 300,
easing = "cubic-bezier(.2,.8,.2,1)",
color = "#4cd4a6",
}: DiffAddedRemovedProps) {
const [open, setOpen] = useState(false);
const [live, setLive] = useState(demo);
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
const cls = [styles.root, live ? styles.isDemo : "", open ? styles.isOpen : ""].join(" ");
function toggle() {
setLive(false);
setOpen(!open);
}
return (
<div className={cls} style={vars}>
<div className={styles.pane}>
<div className={styles.head}>
<span className={styles.ic} aria-hidden="true">
<DiffIcon />
</span>
<span className={styles.file}>{fileName}</span>
</div>
<span className={styles.cursor} aria-hidden="true" />
<div className={styles.body}>
<div className={[styles.line, styles.lineTop].join(" ")}>
<span className={styles.sign} aria-hidden="true">
{" "}
</span>
<span className={styles.src}>
<span className={styles.k}>.card</span> <span className={styles.p}>{"{"}</span>
</span>
</div>
<button
className={styles.fold}
type="button"
aria-expanded={open}
aria-controls="cd-hidden-lines"
aria-label={open ? closeLabel : openLabel}
onClick={toggle}
>
<span className={styles.sign} aria-hidden="true">
{" "}
</span>
<span className={[styles.src, styles.dots].join(" ")}>···</span>
</button>
<div className={styles.foldBody} id="cd-hidden-lines">
<div className={styles.foldIn}>
<div className={styles.line}>
<span className={styles.sign} aria-hidden="true">
{" "}
</span>
<span className={styles.src}>
<span className={styles.k}>display</span>
<span className={styles.p}>:</span> <span className={styles.s}>flex</span>
<span className={styles.p}>;</span>
</span>
</div>
<div className={styles.line}>
<span className={styles.sign} aria-hidden="true">
{" "}
</span>
<span className={styles.src}>
<span className={styles.k}>align-items</span>
<span className={styles.p}>:</span> <span className={styles.s}>center</span>
<span className={styles.p}>;</span>
</span>
</div>
</div>
</div>
<div className={[styles.line, styles.lineDel, styles.lineDel1].join(" ")}>
<span className={styles.sign}>-</span>
<span className={styles.src}>
<span className={styles.k}>padding</span>
<span className={styles.p}>:</span> <span className={styles.n}>12px 16px</span>
<span className={styles.p}>;</span>
</span>
</div>
<div className={[styles.line, styles.lineDel, styles.lineDel2].join(" ")}>
<span className={styles.sign}>-</span>
<span className={styles.src}>
<span className={styles.k}>border-radius</span>
<span className={styles.p}>:</span> <span className={styles.n}>4px</span>
<span className={styles.p}>;</span>
</span>
</div>
<div className={[styles.line, styles.lineAdd, styles.lineAdd1].join(" ")}>
<span className={styles.sign}>+</span>
<span className={styles.src}>
<span className={styles.k}>padding</span>
<span className={styles.p}>:</span> <span className={styles.n}>16px 20px</span>
<span className={styles.p}>;</span>
</span>
</div>
<div className={[styles.line, styles.lineAdd, styles.lineAdd2].join(" ")}>
<span className={styles.sign}>+</span>
<span className={styles.src}>
<span className={styles.k}>border-radius</span>
<span className={styles.p}>:</span> <span className={styles.n}>12px</span>
<span className={styles.p}>;</span>
</span>
</div>
<div className={[styles.line, styles.lineTop].join(" ")}>
<span className={styles.sign} aria-hidden="true">
{" "}
</span>
<span className={styles.src}>
<span className={styles.p}>{"}"}</span>
</span>
</div>
</div>
</div>
</div>
);
}04 더한 줄과 뺀 줄을 나눠 보는 diff — 측정 누적면적 28.963% · 움직인 프레임 17/23GODRICH 원작 · zip 에 SCSS·React 포함 · 코드 블록 UI 9종 — 하이라이터 없이 붙여 쓰는 코드 창
이 데모가 속한 글
코드 블록 UI 9종 — 하이라이터 없이 붙여 쓰는 코드 창코드 블록은 문서 안에 프로그래밍 언어를 그대로 옮겨 담은 상자이고, 아홉 가지 모두 복사·전환·펼침·타이핑 같은 동작이 실제로 붙어 있는 화면입니다. 이 글의 데모 전부 보기


