make template.typ
This commit is contained in:
+597
@@ -0,0 +1,597 @@
|
||||
// ==================================================
|
||||
// Config Variables
|
||||
// ==================================================
|
||||
|
||||
#let font-body = "Pretendard"
|
||||
#let font-serif = ("Source Serif 4", "Source Han Serif K")
|
||||
#let font-display = ("Source Serif 4 Display", "Source Han Serif K")
|
||||
#let font-subhead = ("Source Serif 4 Subhead", "Source Han Serif K")
|
||||
#let font-caption = ("Source Serif 4 Caption", "Source Han Serif K")
|
||||
// #let font-code =
|
||||
|
||||
#let color-surface = white
|
||||
#let color-ink = color.oklch(22%, 0.01, 250deg)
|
||||
#let color-ink-inverse = color.oklch(97%, 0.01, 250deg)
|
||||
#let color-ink-secondary = color.oklch(60%, 0.01, 250deg)
|
||||
#let color-ink-helper = color.oklch(50%, 0.01, 250deg)
|
||||
#let color-ink-faint = color.oklch(70%, 0.01, 250deg)
|
||||
#let color-rule-faint = color.oklch(85%, 0.01, 250deg)
|
||||
|
||||
#let bleed = 1.5cm
|
||||
|
||||
// ==================================================
|
||||
// Metadata
|
||||
// ==================================================
|
||||
|
||||
#let meta = state(
|
||||
"meta",
|
||||
(
|
||||
cur_domain: 0,
|
||||
total_domain: 0,
|
||||
domain_name: "",
|
||||
domain_detail: "",
|
||||
date: "",
|
||||
author: "",
|
||||
),
|
||||
)
|
||||
|
||||
#let color-palette = state(
|
||||
"color-palette",
|
||||
(
|
||||
cover-surface: white,
|
||||
callout-surface: white,
|
||||
callout-border: black,
|
||||
rule: black,
|
||||
problem: black,
|
||||
accent: black,
|
||||
accent-faint: white,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
// ==================================================
|
||||
// Util functions
|
||||
// ==================================================
|
||||
|
||||
#let pad-zeros(num, width) = {
|
||||
let s = str(num)
|
||||
"0" * calc.max(0, width - s.len()) + s
|
||||
}
|
||||
|
||||
#let make-palette(hue) = (
|
||||
cover-surface: color.oklch(97%, 0.01, hue),
|
||||
callout-surface: color.oklch(97%, 0.02, hue),
|
||||
callout-border: color.oklch(85%, 0.05, hue),
|
||||
rule: color.oklch(90%, 0.02, hue),
|
||||
problem: color.oklch(75%, 0.04, hue),
|
||||
accent: color.oklch(40%, 0.09, hue),
|
||||
accent-faint: color.oklch(70%, 0.02, hue),
|
||||
)
|
||||
|
||||
|
||||
// ==================================================
|
||||
// Body components
|
||||
// ==================================================
|
||||
|
||||
#let callout(bg_color: none, border_color: none, body) = context {
|
||||
let cp = color-palette.get()
|
||||
let bg_c = if bg_color == none { cp.callout-surface } else { bg_color }
|
||||
let border_c = if border_color == none { cp.callout-border } else { border_color }
|
||||
|
||||
let x_pad = 12pt
|
||||
let y_pad = 18pt
|
||||
let gap = 24pt
|
||||
|
||||
block(
|
||||
width: 100%,
|
||||
above: gap + y_pad,
|
||||
below: gap + y_pad,
|
||||
outset: (x: x_pad, y: y_pad),
|
||||
radius: 4pt,
|
||||
fill: bg_c,
|
||||
stroke: (paint: border_c),
|
||||
)[
|
||||
#body
|
||||
]
|
||||
}
|
||||
|
||||
#let block_quote(color: none, content) = context {
|
||||
let gutter = 15pt
|
||||
let line-width = 2pt
|
||||
let c = if color != none { color } else { color-palette.get().accent }
|
||||
|
||||
layout(size => {
|
||||
let body-width = size.width - line-width - gutter
|
||||
|
||||
let body-size = measure(
|
||||
content,
|
||||
width: body-width,
|
||||
)
|
||||
|
||||
grid(
|
||||
columns: (line-width, 1fr),
|
||||
gutter: gutter,
|
||||
align: horizon,
|
||||
|
||||
line(
|
||||
stroke: (
|
||||
paint: c,
|
||||
thickness: line-width,
|
||||
cap: "round",
|
||||
),
|
||||
angle: 90deg,
|
||||
length: body-size.height + 15pt,
|
||||
),
|
||||
)[
|
||||
#content
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
#let kicker(color: color-ink-helper, content) = {
|
||||
set text(
|
||||
font: font-body,
|
||||
weight: 700,
|
||||
size: 10pt,
|
||||
tracking: 1pt,
|
||||
fill: color,
|
||||
)
|
||||
|
||||
block(below: 1.5em)[
|
||||
#content
|
||||
]
|
||||
}
|
||||
|
||||
#let problem(content) = context {
|
||||
let c = color-palette.get().problem
|
||||
|
||||
block_quote(color: c)[
|
||||
#kicker(color: c)[문제]
|
||||
|
||||
#content
|
||||
]
|
||||
}
|
||||
|
||||
#let solution(content) = context {
|
||||
let c = color-palette.get().accent
|
||||
|
||||
block_quote(color: c)[
|
||||
#kicker(color: c)[해결책]
|
||||
|
||||
#content
|
||||
]
|
||||
}
|
||||
|
||||
#let hl(content) = context {
|
||||
let cp = color-palette.get()
|
||||
|
||||
// if content == "" {}
|
||||
|
||||
set text(
|
||||
font: font-body,
|
||||
weight: 700,
|
||||
fill: cp.accent,
|
||||
tracking: 1pt,
|
||||
size: 10pt,
|
||||
)
|
||||
|
||||
grid(
|
||||
columns: (1fr, auto, 1fr),
|
||||
align: horizon,
|
||||
gutter: 10pt,
|
||||
line(length: 100%, stroke: (paint: cp.rule)), content, line(length: 100%, stroke: (paint: cp.rule)),
|
||||
)
|
||||
}
|
||||
|
||||
#let block_equation(content) = {
|
||||
callout()[
|
||||
#content
|
||||
]
|
||||
}
|
||||
|
||||
#let stack(layers) = context {
|
||||
let cp = color-palette.get()
|
||||
let radius = 12pt
|
||||
let gutter = 12pt
|
||||
let gap = 24pt
|
||||
|
||||
for (idx, (title, body)) in layers.enumerate() {
|
||||
let is_last = idx == layers.len() - 1
|
||||
|
||||
let content = {
|
||||
v(radius - 12pt / 2)
|
||||
text(
|
||||
weight: 700,
|
||||
)[
|
||||
#title
|
||||
#linebreak()
|
||||
]
|
||||
body
|
||||
v(gap)
|
||||
}
|
||||
|
||||
block(
|
||||
above: if idx == 0 { auto } else { 0pt },
|
||||
below: if is_last { auto } else { 0pt },
|
||||
width: 100%,
|
||||
)[
|
||||
#layout(size => {
|
||||
let content_width = size.width - radius * 2 - gutter
|
||||
let content_size = measure(content, width: content_width)
|
||||
|
||||
grid(
|
||||
columns: (radius * 2, 1fr),
|
||||
gutter: gutter,
|
||||
grid(
|
||||
rows: (radius * 2, auto),
|
||||
circle(radius: radius, fill: cp.accent)[
|
||||
#set align(center + horizon)
|
||||
#set text(font: font-serif, fill: color-ink-inverse, size: 12pt, weight: 700)
|
||||
#{ idx + 1 }
|
||||
],
|
||||
line(
|
||||
start: (50%, 0pt),
|
||||
end: (50%, content_size.height - radius * 2),
|
||||
stroke: (
|
||||
paint: cp.accent,
|
||||
thickness: 1pt,
|
||||
cap: "round",
|
||||
),
|
||||
),
|
||||
),
|
||||
content,
|
||||
)
|
||||
})
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#let selection(choices) = context {
|
||||
let cp = color-palette.get()
|
||||
|
||||
for (name, selected, body) in choices {
|
||||
let status = if selected [채택] else [기각]
|
||||
let bg_color = if selected { cp.callout-surface } else { color-surface }
|
||||
let border_color = if selected { cp.callout-border } else { color-rule-faint }
|
||||
let name_color = if selected { cp.accent } else { cp.accent-faint }
|
||||
let body_color = if selected { color-ink } else { color-ink-faint }
|
||||
|
||||
callout(bg_color: bg_color, border_color: border_color)[
|
||||
#text(
|
||||
weight: 700,
|
||||
fill: name_color,
|
||||
)[
|
||||
#{ name }#h(0.5em)#text(size: 10pt)[#status]
|
||||
#linebreak()
|
||||
]
|
||||
#text(
|
||||
fill: body_color,
|
||||
)[
|
||||
#body
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#let header_template(dark: false, font_color: none, show_activity: false) = place(
|
||||
top + left,
|
||||
dy: 1cm,
|
||||
dx: -bleed,
|
||||
)[
|
||||
#let fc = if font_color != none { font_color } else if dark == true { color-ink-helper } else { color-ink-helper }
|
||||
|
||||
#block(width: 100% + 2 * bleed)[
|
||||
#text(
|
||||
font: font-caption,
|
||||
size: 10pt,
|
||||
fill: fc,
|
||||
tracking: 0pt,
|
||||
)[
|
||||
#context {
|
||||
let m = meta.get()
|
||||
link((page: 1, x: 0pt, y: 0pt))[#m.cur_domain. #m.domain_name]
|
||||
h(1fr)
|
||||
if show_activity {
|
||||
let level1s = query(heading.where(level: 1, outlined: true))
|
||||
let n = counter(heading).get().at(0)
|
||||
let cur = level1s.at(n - 1)
|
||||
let prefix = str(m.cur_domain) + "." + str(n) + ". "
|
||||
link(
|
||||
cur.location(),
|
||||
prefix + cur.body,
|
||||
)
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
#let footer_template(dark: false, font_color: none) = context {
|
||||
let fc = if font_color != none { font_color } else if dark == true { color-ink-helper } else { color-ink-helper }
|
||||
|
||||
align(center)[
|
||||
#set text(
|
||||
size: 10pt,
|
||||
fill: fc,
|
||||
)
|
||||
|
||||
#counter(page).display("1")
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
// ==================================================
|
||||
// Page template
|
||||
// ==================================================
|
||||
|
||||
// Cover template function
|
||||
#let cover() = context {
|
||||
let cp = color-palette.get()
|
||||
|
||||
page(
|
||||
fill: cp.cover-surface,
|
||||
)[
|
||||
#set par(spacing: 0pt)
|
||||
|
||||
#v(3cm)
|
||||
|
||||
#context {
|
||||
let m = meta.get()
|
||||
|
||||
align(center)[
|
||||
#set text(
|
||||
fill: cp.accent,
|
||||
)
|
||||
|
||||
#text(font: font-caption)[
|
||||
#pad-zeros(m.cur_domain, 2) / #pad-zeros(m.total_domain, 2)
|
||||
]
|
||||
|
||||
#v(12pt)
|
||||
|
||||
#text(font: font-display, weight: 700, size: 40pt)[#m.domain_name]
|
||||
|
||||
#v(20pt)
|
||||
|
||||
#text(font: font-subhead, size: 15pt)[#m.domain_detail]
|
||||
]
|
||||
|
||||
align(center + bottom)[
|
||||
#text(weight: 700, size: 10pt, tracking: 1pt)[
|
||||
#m.date
|
||||
#v(10pt)
|
||||
#upper(m.author)
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
#v(1cm)
|
||||
]
|
||||
}
|
||||
|
||||
// Table of contents templage function
|
||||
#let table_of_contents() = page(
|
||||
footer: footer_template(),
|
||||
)[
|
||||
#context {
|
||||
let m = meta.get()
|
||||
heading(level: 1, outlined: false, numbering: none)[#m.cur_domain. #m.domain_name]
|
||||
}
|
||||
|
||||
#outline(title: none, depth: 2)
|
||||
]
|
||||
|
||||
// Domain frame template function
|
||||
#let domain_frame(bg_img_path: none, font_color: color-ink, chrome_color: color-ink-helper, content) = page(
|
||||
background: place(
|
||||
center + horizon,
|
||||
if bg_img_path != none {
|
||||
image(bg_img_path, width: 100.5%, height: 100.5%, fit: "cover")
|
||||
},
|
||||
),
|
||||
header: header_template(font_color: chrome_color),
|
||||
footer: footer_template(font_color: chrome_color),
|
||||
)[
|
||||
#set text(fill: font_color)
|
||||
#content
|
||||
]
|
||||
|
||||
// Activity cover template function
|
||||
#let activity_cover(name: "", hook: "", content) = page(
|
||||
header: header_template(),
|
||||
footer: footer_template(),
|
||||
)[
|
||||
#heading(level: 1)[
|
||||
#name
|
||||
]
|
||||
|
||||
#block_quote()[
|
||||
#hook
|
||||
]
|
||||
|
||||
#content
|
||||
]
|
||||
|
||||
// Activity intro template function
|
||||
#let activity_intro(title: "", content) = page(
|
||||
header: header_template(show_activity: true),
|
||||
footer: footer_template(),
|
||||
)[
|
||||
#kicker("프로젝트 설명")
|
||||
|
||||
#heading(level: 2)[
|
||||
#title
|
||||
]
|
||||
|
||||
#content
|
||||
]
|
||||
|
||||
// Activity process
|
||||
#let activity_process(title: "", content) = page(
|
||||
header: header_template(show_activity: true),
|
||||
)[
|
||||
#kicker("시행착오")
|
||||
|
||||
#heading(level: 2)[
|
||||
#title
|
||||
]
|
||||
|
||||
#content
|
||||
]
|
||||
|
||||
// Activity result
|
||||
#let activity_result(title: "", content) = page(
|
||||
header: header_template(show_activity: true),
|
||||
)[
|
||||
#kicker("결론")
|
||||
|
||||
#heading(level: 2)[
|
||||
#title
|
||||
]
|
||||
|
||||
#content
|
||||
]
|
||||
|
||||
|
||||
// ==================================================
|
||||
// Style Settings
|
||||
// ==================================================
|
||||
|
||||
#let base(
|
||||
cur_domain: 0,
|
||||
total_domain: 0,
|
||||
domain_name: "",
|
||||
domain_detail: "",
|
||||
date: "",
|
||||
author: "",
|
||||
main-hue: 0deg,
|
||||
doc,
|
||||
) = {
|
||||
// meta data
|
||||
meta.update(m => (
|
||||
m
|
||||
+ (
|
||||
cur_domain: cur_domain,
|
||||
total_domain: total_domain,
|
||||
domain_name: domain_name,
|
||||
domain_detail: domain_detail,
|
||||
date: date,
|
||||
author: author,
|
||||
)
|
||||
))
|
||||
|
||||
// color palette
|
||||
let cp = make-palette(main-hue)
|
||||
color-palette.update(m => (m + cp))
|
||||
|
||||
// document & page config
|
||||
set document(
|
||||
title: str(cur_domain) + ". " + domain_name,
|
||||
description: domain_detail,
|
||||
author: author,
|
||||
)
|
||||
|
||||
set page(
|
||||
paper: "a4",
|
||||
margin: (x: 2.8cm, y: 4.2cm),
|
||||
fill: color-surface,
|
||||
)
|
||||
|
||||
// text config
|
||||
set text(
|
||||
font: font-body,
|
||||
weight: 400,
|
||||
fill: color-ink,
|
||||
size: 12pt,
|
||||
)
|
||||
|
||||
set par(
|
||||
leading: 1.2em,
|
||||
spacing: 2.4em,
|
||||
)
|
||||
|
||||
show strong: set text(fill: cp.accent)
|
||||
|
||||
// list config
|
||||
show list: set par(leading: 0.8em)
|
||||
set list(
|
||||
marker: ([--], [•]),
|
||||
indent: 0.6em,
|
||||
body-indent: 0.8em,
|
||||
spacing: 1.2em,
|
||||
)
|
||||
|
||||
show enum: set par(leading: 0.8em)
|
||||
set enum(
|
||||
indent: 0.6em,
|
||||
body-indent: 0.8em,
|
||||
spacing: 1.2em,
|
||||
)
|
||||
|
||||
// heading config
|
||||
show heading.where(level: 1): set heading(numbering: (..nums) => (
|
||||
str(cur_domain) + "." + nums.pos().map(str).join(".") + "."
|
||||
))
|
||||
show heading.where(level: 1): set block(above: 0em, below: 50pt)
|
||||
show heading.where(level: 1): set text(
|
||||
font: font-display,
|
||||
weight: 700,
|
||||
size: 27pt,
|
||||
tracking: 0pt,
|
||||
)
|
||||
|
||||
show heading.where(level: 2): set block(above: 0em, below: 50pt)
|
||||
show heading.where(level: 2): set par(leading: 1em)
|
||||
show heading.where(level: 2): set text(
|
||||
font: font-subhead,
|
||||
weight: 700,
|
||||
size: 18pt,
|
||||
tracking: 0pt,
|
||||
fill: color-ink,
|
||||
)
|
||||
|
||||
// outline config
|
||||
set outline(
|
||||
indent: 2em,
|
||||
)
|
||||
|
||||
show outline: set par(leading: 1em)
|
||||
|
||||
show outline.entry: it => link(
|
||||
it.element.location(),
|
||||
)[
|
||||
#grid(
|
||||
columns: (1fr, auto),
|
||||
it.indented(it.prefix(), it.body()), it.page(),
|
||||
)
|
||||
]
|
||||
|
||||
show outline.entry.where(level: 1): it => {
|
||||
set text(
|
||||
font: font-subhead,
|
||||
size: 15pt,
|
||||
weight: 700,
|
||||
)
|
||||
|
||||
block(above: 50pt)[
|
||||
#it
|
||||
#line(length: 100%, stroke: (paint: color-ink-secondary, thickness: 1pt))
|
||||
]
|
||||
}
|
||||
|
||||
show outline.entry.where(level: 2): it => {
|
||||
set text(
|
||||
font: font-body,
|
||||
size: 12pt,
|
||||
weight: 700,
|
||||
fill: color-ink-secondary,
|
||||
)
|
||||
|
||||
block(above: 20pt)[
|
||||
#it
|
||||
]
|
||||
}
|
||||
|
||||
// render content
|
||||
doc
|
||||
}
|
||||
Reference in New Issue
Block a user