This page is
brand/en/design-language.mdin kitDesign (the English translation ofbrand/KIT設計語言.md, which is the source of truth). To see what the components look like, see Components.
Easy to see, safe to click, results first.
The design language shared by the KIT research tools (kitJoin, kitsUI, and future Shiny apps). New apps follow this document, so once people learn one tool, they can use the others.
-
Companions:
- Implementation: the R package kitDesign
(
kit_page(), components, modules, the CSS ininst/www/). This document lives in kitDesign’sbrand/, and this copy is the source of truth. - Design system (live samples, tokens, logo): https://claude.ai/artifact/GFN5Q5ie9jAHKwmsBaxb2M (private; visible only when shared)
- _brand.yml: design variables such as colors and fonts; logo/: the logo (SVG, PNG)
- What every component looks like:
kitDesign::kit_gallery()
- Implementation: the R package kitDesign
(
-
Status: v1.2 (2026-11-01). v1.2 adds kitDesign
0.7.0’s interactive table (
kit_dt()) and the scope bar (ScopeBar,kit_scope_bar()). v1.0 merged v0.2’s “additions after design review” into the main text; v1.1 adds kitDesign 0.2.0’s charts (kit_plotly_style()), the danger button, the heatmap color scale, the step card rule “choose first, refine later”, and “Author’s decisions” 6–9. Where the spec and the code differ, this document follows the code; the differences are listed in the last section.
Who we design for
| Who | Researchers and research assistants, mostly aged 40–60; they use SPSS and Excel, and may not write R |
| How often | A few times a year, an hour or two each time; every return feels like the first time |
| Doing what | Working with real research data: joining, converting, calibrating, scoring. Wrong results end up in papers |
| Where | Desktops or laptops in the office, 13–27 inch screens; sometimes a phone to check results |
| Biggest fears | Not knowing what to click next; clicking and not knowing whether it worked; results that look right but are wrong |
Priority: correct > clear > good-looking. Looking good makes people willing to use the tool and comfortable reading it, but it must never cost the first two. Keep the design simple and clean.
Seven principles
-
One lead per page. Each page has exactly one
primary button (
nb-lime), for example “開始校準” (“Start calibration”). Everything else is supporting cast and steps back visually. - Results first, details later. After a click, the first thing people see is the status box: a one-sentence conclusion, two or three key points, and one table or one chart. Parameter tables, logs, check tables, and R code go in the details row, opened on demand.
- Problems must be seen. The most dangerous case is “looks successful but is actually wrong”. If anything needs attention, the status cannot be a plain 【完成】 (“Done”). Actions that change results (skipping a scale, treating codes as missing values) are clicked by the user, never done automatically.
- A frame is a unit. Anything with a black frame is either something you can operate or a complete unit (a button, an input, one result, one chart). No frames inside frames, and no decorative frames.
-
Quiet supporting cast. Explanations, hints, and
technical details use the
hintsize andtext-hintcolor, no frame, and are collapsed by default. First-time users can find them; experienced users are not slowed down. - Every page looks the same. The same page skeleton, the same components, the same wording. Learn one page and you can use the others; learn kitJoin and you can use kitsUI.
- Plain language first. The main screen uses short sentences in the Chinese commonly used in Taiwan. English terms (function names, column names) go in the details or in downloaded files.
Visual foundations
Style: restrained neo-brutalism
We keep three traits: black frames, offset shadows with zero blur, and flat color blocks with black text. Everything is toned down for middle-aged users and long reading sessions:
| Typical neo-brutalism | The KIT way | Why |
|---|---|---|
| 3–5px frames |
bw-frame 2px |
Matches the stroke weight of the rounded typeface; easy on the eyes over time |
| 6–8px shadows |
shadow-header 4px, shadow-card 3px,
shadow-sm 2px |
Shadows only mean “clickable” or “a result” |
| Large areas of saturated color | Saturated color only on the header, the primary button, and the current step | Large bright areas are tiring over time |
| Deliberate misalignment | Aligned, regular spacing | A tool should be predictable |
Color
Colors are defined by role, and each role has exactly one color.
-
Background: the whole page is
nb-page(off-white); cards, inputs, tables, and charts usenb-card. Light only; there is no dark mode. -
Ink: all text, frames, and shadows are
nb-ink. Text on color blocks is alwaysnb-ink, never white. The only white text is the black header row of the main data table. -
Lead:
nb-limeis used only for the header, the primary button, the current tab, the current step number, and the selected item in a dropdown. Never use it as a text color. -
Brand and links:
nb-green(the same color as the logo’s dark green) is for links, progress bars, ✓, and checkboxes. On hover,nb-green-hover. Never use it as a large background. -
Four semantic colors; seeing the color tells you
the meaning:
-
nb-mintdone -
nb-yellowneeds attention (also used for 【要重新執行】 (“Run again”)) -
nb-salmonstopped (also used for the danger button) -
nb-skyinformation
-
- Color must never be the only signal. The status box always has a text label: 【完成】 (“Done”), 【完成,但請注意】 (“Done, but note”), 【停下來】 (“Stopped”), 【要重新執行】 (“Run again”); the ✓ on tabs and the ! in checklists both carry hidden text.
-
These are the only grays:
- Supporting text
text-hint - Disabled text
text-disabled - Lines
line,line-soft - Disabled frames
border-disabled
- Supporting text
-
Emphasis text: “還差:…” (“Still needed: …”) uses
text-todo; the checklist ! usestext-decide; a single problem in a list usestext-stop. -
Logo colors (
logo-bean,logo-leaf) are only for the logo and illustrations, never for the interface. -
Charts: categorical colors use the
colorblind-friendly Okabe–Ito palette, taken in order:
chart-blue,chart-orange,chart-green,chart-purple,chart-sky,chart-vermillion. Reference lines and bands usechart-neutral; continuous values usechart-sequential.
Typography
- One rounded typeface throughout: Chiron GoRound TC
(
sans, Google Fonts, weights 400, 700, 900).- Hierarchy comes from weight and size, not from switching typefaces.
- Use weights 400 and 700; the header h1 may go to 900. Secondary buttons and “no problem” checklist items use 500.
- Code, raw messages, and values in diagrams: JetBrains
Mono (
mono), falling back to the rounded typeface for Chinese. Ligatures are always off (font-variant-ligatures: none), so<-and|>display as typed. - Six type sizes, base 17px
(
html { font-size: 17px }):-
display: header h1 -
h4: section titles -
h5: subtitles, step card titles, the primary button, the first line of the status box -
body: body text -
small: tables, step summaries -
hint: hints, collapsible titles, group labels, the version line
-
- The smallest size is
hint(about 14px); anything smaller is for chart tick labels only. - Put a half-width space between Chinese and English or numbers: 「688 人」, 「依 SPSS 數值標籤」. Numbers of four digits or more get thousands separators: 「3,389 人」.
Frames, shadows, corners
-
Frames:
- Top-level components use
bw-frame(2px)nb-ink. - Table row dividers and secondary buttons use
bw-line(1.5px). - Diagram grid lines use
bw-hair(1px).
- Top-level components use
-
Corners: everything
radius(8px); progress barsradius-bar; step numbersradius-round. -
Shadows, from largest to smallest:
-
shadow-header: the header only. -
shadow-card: results, including the status box, charts, the current step card, and the primary button. -
shadow-sm: clickable things, including tabs, regular buttons, dropdowns, and focused inputs. - No shadow: callouts, code, inputs (when not focused), finished or not-yet-available step cards, tables, secondary buttons. A callout is not a result, so it has no shadow; that keeps the status box always the most prominent box on the page.
-
-
Interaction:
- On hover, move 1px down and to the right; the shadow becomes
shadow-hover. - On press, move all the way and drop the shadow; animation 80ms.
- The current tab looks “pressed”: lime, no shadow, offset 2px.
- When the user has set “reduce motion”, no offset and no animation.
- On hover, move 1px down and to the right; the shadow becomes
-
Keyboard focus: 3px
focusoutline, offset 3px outward. Tabs, file buttons, and checkboxes all need it.
Spacing and layout
- Spacing uses a 0.25rem unit, from
sp-1(0.25rem) tosp-5(2.4rem).- Between sections:
sp-5. - Between components in the same section:
sp-3. - Between buttons in a row:
sp-2.
- Between sections:
- Content is at most
content-max(1140px) wide, centered;gutteron the left and right, 1rem at ≤bp-tablet. - When settings and results sit side by side: settings on the left
1/3, results on the right 2/3; at ≤
bp-tabletthey stack, settings on top. - Tables are as wide as their content, not stretched; at most the layout width, and they scroll sideways inside their own frame when wider.
Icons
- Text symbols only:
- ✓: done, no problem
- !: needs a decision
- i: reminder
- →: next step, leaves this page
- ▸/▾: collapse
- ↑↓: sort
- ⬇: download
- ①–④: only for tabs of an ordered workflow
- No emoji and no other icon fonts.
- Icons always come with text; symbols next to text get
aria-hidden="true". - Each symbol has exactly one meaning.
Logo
- The header uses the full logo
kit-logo(bean + KIT wordmark), 5.6rem tall; 3.6rem on phones. The browser tab icon uses only the bean,kit-mark. - Leave clear space of at least 1/4 of the logo height on all sides.
It may sit on
nb-lime,nb-page, ornb-card; not on status colors ornb-green. - Do not change its colors or proportions, and do not add frames or
shadows. Use
alt=""when the tool name is already next to it.
Components
Each component lists its purpose, R function, appearance, rules, and don’ts. Live samples are on the design system’s components page.
Header
The header tells users which tool this is and which version; an app has exactly one, at the very top.
-
R:
kit_header()(kit_theme.R), reads Version and Date from DESCRIPTION. -
Appearance:
-
nb-limeblock,bw-frameframe,shadow-header. - Full logo on the left (5.6rem tall); on the right the tool name
(
display), one line of tagline (bold), and one line of version (hint).
-
- You provide: the tool name (“KIT ×× 工具”, “KIT ×× tool”) and the tagline (database name · what this tool does).
-
Rules:
- The version line is always there; it is needed when reporting problems.
- Do not change the tool name when testing with other data.
- Phones (≤ 576px): logo 3.6rem, h1 1.35rem.
- Don’t: put buttons or navigation in the header; change the logo’s colors or proportions, or put a frame around it.
Step tabs (NavTabs)
Step tabs are the main navigation inside a tool: a row of clickable blocks; the current one is lime, finished ones get a ✓, and not-yet-available ones are gray.
-
Two kinds:
- Ordered workflow (kitsUI): ① 資料 → ② 題目校準 → ③ 連結 → ④ 受試者評分 (① Data → ② Item calibration → ③ Linking → ④ Respondent scoring), with circled numbers.
-
Not every page is required (kitJoin): three groups,
unnumbered, each with a
hintgroup label above it:- 準備 (Prepare)
- 做出資料(選一個) (Make the data (pick one))
- 其他工具 (Other tools)
-
R: kitsUI uses
navset_tabplus the states fromkit_tabs_js(); kitJoin draws the tab bar withkit_navgroups(), and the real tabs are atabsetPanel(type = "hidden"), soupdateTabsetPanel()works as usual. -
Appearance:
- White background,
bw-frameframe,shadow-sm. - Current tab
nb-lime, no shadow, moved 2px down and to the right (the pressed look). - Standalone small tools and “使用手冊” (“User guide”): dashed frame, no shadow, placed at the far right.
- White background,
-
States:
- Done: ✓ (
nb-green), plus hidden text 「(已完成)」 (“(done)”). - Not yet available:
bg-disabledbackground,text-disabledtext; on hover, explains what must be done first. - Needs re-running: the ✓ is removed.
- Done: ✓ (
-
Rules:
- Tab names are “noun + verb”: 檔案上傳 (file upload), 遺漏值處理 (missing value handling).
- At ≤ 768px the tabs stay in one row and scroll sideways; they do not wrap.
- Don’t: wrap the tabs in another big frame; use numbers that imply every step is required when the steps are actually alternatives.
Scope bar (ScopeBar)
The scope bar is one choice shared by the whole app (for example which item bank kitItemBank uses). Every tab depends on it, so it sits right under the tab bar, not inside one page.
-
R:
kit_scope_bar(input, line = NULL, actions = NULL, panel = NULL):inputisradioButtons(..., inline = TRUE)(2–5 choices) orselectInput();lineis one hint line;actionsare small buttons on the right;panelis what must be confirmed after choosing. -
Appearance:
- No frame, no shadow; a faint
linerule (1.5px) below separates it from the page;sp-3/sp-4above and below. - First row: the group name (bold) and a row of radio buttons.
- Second row:
hinttext saying what is in use and how big it is (「目前:TDCYP 題庫 · 37 題 · 4 個波次」 (“In use: TDCYP item bank · 37 items · 4 waves”)), key words in boldnb-ink; something still to do in boldtext-todo(.kit-scopebar-todo, 「還差:上傳題庫檔」 (“Still needed: upload the item bank file”)). - Small buttons (download, new, upload) on the right of the second row; on a phone they follow the hint, left-aligned.
- When the choice needs confirming (for example “upload your own item
bank”), a confirmation area opens below
(
.kit-scopebar-panel): a checklist and a row of buttons (.kit-buttons), at most 46rem wide.
- No frame, no shadow; a faint
-
Rules:
- At most one per app, and only for a choice that affects every tab. Settings for one page stay in that page’s settings.
- When the scope changes, results that used the old scope become 【要重新執行】 (“run again”; see State flow).
- When changing the scope takes time or loses something, say so in the confirmation area first, and change only when the button is pressed.
- Don’t: make it a tab or a card; put a primary (lime) button in it; put two choices in one bar.
Page guide (“What this page does”) (PageGuide)
“這一頁做什麼” (“What this page does”) is a one-line explanation at the top of each tab, collapsed by default; first-time users open it to see the steps.
-
R:
kit_tab_guide(goal, steps, output, technical = NULL, note = NULL, diagram = NULL). -
Appearance:
- No frame,
hintsize,text-hintcolor, with a faintlinerule below. - “看步驟 ▸” (“See steps ▸”) is underlined
nb-greentext.
- No frame,
-
You provide:
-
goal: one sentence, at most 30 characters. -
steps: 3–6 points. -
output: what you get. - Optional:
notefor extra notes,technical(one more collapsed level),diagram(see Diagram).
-
- Don’t: open it by default; put settings or buttons inside it; make it a card.
Button
There are four kinds of buttons, and each page has only one primary button; labels start with a verb and say clearly what happens on click.
-
R:
kit_button(id, label, kind = "secondary"), wherekindis"primary","secondary", or"danger"; a regular button is just the default look ofshiny::actionButton(). -
Primary button (
btn-primary):-
nb-limebackground,h5size,shadow-card, height ≥ 48px. - One per page, plus the “下一步” (“Next step”) at the bottom.
-
-
Regular button (
actionButton()default): white background,bw-frameframe,shadow-sm, height ≥ 40px. Used for in-flow buttons such as “對,下一步” (“Yes, next step”). -
Secondary button (
kind = "secondary",btn-sm btn-outline-secondary):- White background,
bw-lineframe, no shadow, 0.85rem, weight 500, height ≥ 36px. - Used for download, select all, example files, move up/down, copy.
- White background,
-
Danger button (
kind = "danger",btn-sm btn-danger):-
nb-salmonbackground,nb-inktext,bw-lineframe, same size as the secondary button; staysnb-salmonon hover and press. - When disabled it uses the disabled look (
bg-disabledbackground), not salmon. - Used for delete, remove, and “這一回合不做” (“Skip this round”).
-
-
Disabled:
bg-disabledbackground,text-hinttext,border-disabledframe, no shadow; do not use opacity. A disabled primary button always has a “還差:…” (“Still needed: …”) line above it (see ReadyLine). -
Rules:
- Buttons or links that leave the page end with “→”.
- The recommended option goes first and says 「(建議)」 (“(recommended)”).
- When a download is the lead of the page (for example, a response
file), use
kit-download btn-primary.
- Don’t: write 「確定」 (“OK”) or 「送出」 (“Submit”); put two lime buttons on one page; use white text.
Download button (DownloadButton)
The download button looks the same as a secondary button (⬇ icon + 「下載 CSV」, “Download CSV”), but the file name is saved correctly in every browser, Chinese names included.
-
R:
kit_download_button(session, id, label, filename, content, class = NULL), placed insiderenderUI(); in tests, click download withkit_test_download(session, id). -
Appearance:
<a class="btn btn-default kit-download">, styled as a secondary button; addclass = "btn-primary"when it is the lead of the page. -
Rules:
- The file name is decided when the button is drawn; the last segment
of the URL,
Content-Disposition(filenameand the UTF-8filename*), and thedownloadattribute all use the same file name. - The file is written only when download is clicked; if it cannot be
written, a red notification appears (it does not disappear on its own)
with the plain-language message from
kit_abort()as is. -
.csv,.zip, and.htmluse their own MIME types; everything else (.sav,.csv.gz) isapplication/octet-stream, so the browser does not add an extra extension. - The app sets
options(kitDesign.app = "kitJoin")once in.onLoad(): it tells the log which app wrote it; when a file name is all Chinese, the ASCII fallback name for older browsers is the lowercase app name (「第一波.sav」 → 「kitjoin.sav」).
- The file name is decided when the button is drawn; the last segment
of the URL,
-
Don’t: use
shiny::downloadHandler()(Chinese file names becomemissing-download_savin some browsers).
Input
Inputs are white with a black frame, the label above and a small hint below; the default value is the most common and safest one.
-
R: Shiny’s own
selectInput(),textInput(),radioButtons(),checkboxInput(); hints use.kit-hint. -
Appearance:
- White background,
bw-frameframe,radius. -
shadow-smappears on focus; when a dropdown is open, the selected item isnb-lime. - Checkboxes and radio buttons are always 20px,
nb-greenwhen selected.
- White background,
-
Rules:
- 2–5 options: radio buttons, so all are visible at once; more than 5: a dropdown.
- For required fields, write 「(必填)」 (“(required)”); no red asterisks.
- Advanced options go under 「進階設定 ▸」 (“Advanced settings ▸”) (see Details).
- Values guessed from column names get the pale yellow
bg-guessbackground, reminding users to check them. - One field, two uses: for the same column, some files need “pick a column in the file” and others need “the same value for every row” (cohort TDCYP, wave 2009). Use one dropdown you can type into; do not add a separate mode choice.
- A group of checkboxes needs a group name; more than 8 in a row becomes a table or gets “select all”.
-
Don’t: give inputs a shadow (except on focus); put
dropdowns in a table without an
aria-label.
Status box (StatusBox)
The status box is the first thing to look at after clicking the primary button: four fixed labels, colors, and wordings.
-
R:
kit_status(),kit_done_status()(switches to needs-attention automatically when there are warnings),kit_error_status(e, action, todo),kit_stale_status(run_label); rendered withmake_status_ui(). - Four states:
| State | Label | Background |
|---|---|---|
| Done | 【完成】 (“Done”) | nb-mint |
| Needs attention | 【完成,但請注意】 (“Done, but note”) | nb-yellow |
| Stopped | 【停下來】 (“Stopped”) | nb-salmon |
| Needs redoing | 【要重新執行】 (“Run again”) | nb-yellow |
-
Appearance:
bw-frameframe,shadow-card; the most prominent box on the page. -
Content:
-
First line: label + one-sentence conclusion, bold,
about
h5size. For example 「【完成】串接完成:結果共 3,389 列 × 2,058 欄。」 (“[Done] Join complete: 3,389 rows × 2,058 columns.”) - Then: at most 3 points, things needing attention first, key results after; the rest go under 「還有 N 點」 (“N more”).
- When stopped: always say what to do, with a button or link; R’s original message goes under 「原始訊息」 (“Original message”).
-
First line: label + one-sentence conclusion, bold,
about
-
Rules:
- After a run, scroll the status box to the top of the screen
(
kit_scroll_to()). - The key output below the status box is one table or one chart; do not add another “result summary” box.
- After a run, scroll the status box to the top of the screen
(
- Don’t: invent new labels; put the raw error message at the top level.
Callout
A callout is a reminder you should know before clicking; it is not a result, so it has no shadow.
-
R:
kit_callout(..., warn = FALSE). Cloud-version reminders look the same: the banner below the headerkit_cloud_banner(), and the usage notes at the very bottom of the pagekit_cloud_footer(sections, links)(small gray text; the wording comes from the app); check whether it is the cloud version withkit_is_cloud(). -
Two kinds:
-
Information (
nb-sky): general extra notes, for example 「外部連結不適用:……這不是錯誤」 (“External linking does not apply: … this is not an error”). -
Attention (
warn = TRUE,nb-yellow): for example 「資料裡有疑似遺漏值代碼,建議先到遺漏值處理」 (“The data contain what look like missing-value codes; we suggest going to missing value handling first”).
-
Information (
-
Appearance:
bw-frameframe, no shadow; links inside are boldnb-ink. -
Rules:
- At most one callout per block.
- If it can go in the status box, do not open a separate callout.
- When there is a button, put it outside the frame.
- Don’t: use a callout to report results; put a frame inside the frame.
Data table (DataTable)
The data table is one of the leads of the results: white background, black header row, width follows content; long tables show only the first rows.
-
R:
kit_static_table(df)(integers get thousands separators automatically),kit_preview_table(df, priority_cols, n),kit_rows_note(nrow, ncol, shown_rows); for tables people search, page through and click,kit_dt(df, selection, page_length, search, wrap_cols, min_widths)(insideDT::renderDT()). -
Which one:
-
Result tables (the key table under the status box,
previews, small comparisons):
kit_static_table(). Short, read-only; the full table is downloadable. -
Tables to work with (item lists, search results,
picking rows to act on):
kit_dt(). Sortable, paged, selectable; long tables are not drawn all at once. - Don’t swap a result table for
kit_dt()to make it look more capable: the search box and page buttons are more things to understand.
-
Result tables (the key table under the status box,
previews, small comparisons):
-
Appearance:
- White background,
bw-frameouter frame,nb-inkheader row with white text. - Thin
bw-linelines between rows,bg-stripezebra stripes,smallsize. - Numbers right-aligned, tabular figures.
- White background,
-
Rules:
- An
h4title above, plus onehintline: 「共 157 列,以下前 20 列。」 (“157 rows; the first 20 are shown below.”). The full table is downloadable, or under 「▸ 全部(157 列)」 (“▸ All (157 rows)”). - Width follows content, up to the layout width; when wider, it
scrolls sideways inside
.kit-table-scrolland does not break the layout. - Column names: function output and the data itself keep English names; summary tables the app builds itself may use Chinese; downloaded files keep the original column names.
- Tables inside step cards have no outer frame and use a
bg-stripe(pale) header row, not the black one.
- An
-
Interactive table (
kit_dt()):- Same frame, black header, zebra stripes, row lines and
smallsize as above; numeric columns are right-aligned with tabular figures automatically. Rows are a little denser than in result tables. - Click a header to sort (white ▲▼). When
selectionis not"none", the hovered row isbg-soft-hoverwith a pointer cursor; a selected row isbg-newpale lime with a 4pxnb-greenbar on the left, not blue. - One
hintline below: 「共 37 列,第 1–20 列」 (“37 rows, rows 1–20”); on the right, page buttons that look like small buttons (white,bw-lineframe, ≥ 36px), the current pagenb-lime, unavailable ones in the disabled look. On a phone only 「上一頁」 (previous), the current page and 「下一頁」 (next) remain. - The search box (
search = TRUE) sits above the table on the left, white with abw-frameframe; usually off, with the page’s own conditions above the table. - Long text columns (item text) wrap via
wrap_cols; other columns do not wrap. Width follows content; a table that is too wide scrolls sideways inside its own box, and the page never scrolls sideways. - Headers and page buttons are reachable with the keyboard (Tab) and
show the
focusblue ring. All text is Chinese (上一頁, 下一頁, 沒有符合的資料).
- Same frame, black header, zebra stripes, row lines and
- Don’t: stretch to full page width; show hundreds of rows at once; use DataTables’ default blue selection or gray page buttons.
Chart
Charts sit in a white card, and each chart answers only one question;
categorical colors use Okabe–Ito, taken in order. A plotly chart is
always passed to kit_plotly_style() last, so it looks like
part of the same app.
-
R:
- Colors:
kit_chart_colors()(categorical),kit_chart_neutral()(neutral),kit_chart_sequential()(color scale for continuous values). - Look:
kit_plotly_style(p)(plotly is in Suggests; the app needs its ownImports: plotly); a downloaded single HTML file needs the app’s fonts:kit_plotly_fonts(p)(kit_plotly_style()already attaches them by default). - kitsUI’s
kit_chart.R:kit_chart_categorical()(recolors charts drawn by kits with the categorical colors),kit_chart_role()(role colors).
- Colors:
-
Order when used with kits: kits’
plot()→ kitsUI recoloring (kit_chart_categorical(),kit_chart_role()) →kit_plotly_style(). What kits draws is what you get; the app only applies colors and look.- Recoloring and
kit_plotly_style()can be swapped: it does not touch colors set by kits or kitsUI. - The on-screen chart and the downloaded HTML use the same reactive (styled once), so both are identical.
p <- plot(x$item, type = "category", items = shown) # kits p <- kit_chart_categorical(p) # kitsUI kitDesign::kit_plotly_style(p, filename = "item_category") # last - Recoloring and
-
Appearance (what
kit_plotly_style()does):- Card:
nb-cardbackground,bw-frameframe,shadow-card(CSS.js-plotly-plot); the chart itself has a white background, and so does the downloaded PNG. - Text: all in the rounded typeface (
sans),nb-ink; body 15px, legend and hover box 14px, tick labels 13px intext-hint. - Title: left-aligned, bold, about
h5(18px); wraps in the browser when the chart is too narrow. The kits subtitle (<sup>) stays on the next line. - Axes (every axis, including side-by-side panels): axis line
nb-ink1.5px, only left and bottom (no box on four sides), ticks pointing outward, grid linesline-soft, zero lineline. With a single chart, the kits outer frame is removed; for side-by-side panels (category probability curves, Wright map) the outer frames become faintlinerules, so the panels stay distinguishable. - Hover box: white background,
nb-inkframe, rounded typeface. - Legend: no frame; with more than 4 items, or when the longest item
is wider than 20 columns (a Chinese character counts as 2), it goes
below the chart, horizontal, left-aligned; otherwise on the right. When
the chart is narrower than 600px (phones) it always goes below.
legend = "keep"leaves the position unchanged. - Color scale: heatmap-type traces switch to
kit_chart_sequential()(very pale lime →bg-new→nb-green, darker for larger values; the minimum is not white, so 0% and empty no-data cells stay distinguishable);colorscale = FALSEkeeps the original scale. Color bar text and frame follow the sizes and colors above. - Toolbar: no plotly logo; only “download as PNG”, zoom in, zoom out, and reset to the original range; in Traditional Chinese.
- Margins: equally small on all sides; grows automatically when axes, legend, or color bar do not fit; axis titles and the legend are at least 16px from the card edge, so text is never cut off.
- Left unchanged: data, axis ranges and ticks, specified colors, marker shapes, line widths and dashes, hover and legend text, chart height.
- Card:
-
Palette:
- Cohorts, scales, and scores take
chart-blue,chart-orange,chart-green,chart-purple,chart-sky,chart-vermillionin order. plotly’s own default colors are replaced by the Okabe–Ito color in the same position. - Reference lines, bands, and unjudged points use
chart-neutral. - Continuous values (pass rates and so on) use
chart-sequential.
- Cohorts, scales, and scores take
-
Rules:
- The title above the chart states the question it answers.
- Text on the chart follows kits’ original (English) and is not translated: the screen, the downloaded chart, and the chart from running kits in R are identical and can go straight into a paper. Labels the user typed (item IDs, domains, cohorts, waves, scale names) display as typed, Chinese included.
- Below the chart, one
hintline in Chinese explains how to read it; legend entries needed for a decision (for example Kept and Dropped in the linking chart) get their Chinese meaning in that line. - Leave at least 1.2rem between the chart and the explanation below it; the explanation must not overlap the x axis.
- Download the chart with a secondary button (HTML via
htmlwidgets::saveWidget(); the toolbar can also save a PNG).
- Don’t: use plotly’s default colors or default toolbar; change plotly fonts and axis lines separately in the app; answer two questions in one chart.
Details row, advanced settings, settings summary (Details)
The details row holds the “details” in “results first, details later”: a row of collapsed small-text titles; the open one takes a full line.
-
R:
kit_details_row(...)wraps a row ofkit_detail(title, ..., open = FALSE); advanced options in the settings area usekit_advanced(..., title = "進階設定"); the one-line settings summary above results useskit_settings_line(parts). -
Appearance:
- Titles are small
text-hinttext with ▸, no frame. - Opened content has no frame either; opened advanced settings get a
faint
linerule on the left.
- Titles are small
- You provide: a title for each detail (a noun: 「題目參數」 (“Item parameters”), 「這一回合的紀錄」 (“Log for this round”), 「用 R 怎麼做」 (“How to do it in R”)) and its content.
-
Rules:
- Collapsed by default.
- “用 R 怎麼做” (“How to do it in R”) is always the last item in the details row.
- The settings summary goes above the primary button or the results, one line of small text: 「本回合 R1:M03、M36 · 參照世代 M03 · 沒有固定檔」 (“This round R1: M03, M36 · reference cohort M03 · no anchor file”).
- Don’t: use accordion cards; stack frames inside details.
“How to do it in R” panel (CodePanel)
“用 R 怎麼做” (“How to do it in R”) turns the current settings into R code you can run as is, and always lives in the details row.
-
R: kitJoin’s
kit_code_blocks(snips),kit_code_panel(output_id); kitsUI’s component of the same name. -
Appearance:
- Collapsed, it is one line of small text in the details row: 「▸ 用 R 怎麼做」.
- Opened, from top to bottom:
- One line of explanation: 「以下程式碼依你目前的設定產生,是簡化的示範。」 (“The code below is generated from your current settings and is a simplified example.”)
- The version name (bold) and a 「複製」 (“Copy”) secondary button, on the same line.
- The code.
- Only the code itself has a frame:
bg-codebackground,bw-frameframe, no shadow,codesize. - The code frame is at most the content width of the layout (inside the gutters) and must not break the layout when opened.
-
Rules:
- When there are two versions (tidyverse, base R), the two “version name + code” pairs are stacked.
- Ligatures are always off:
<-and|>display as typed, so copying by hand does not introduce mistakes. -
At most about 70 columns per line: for both code
examples and explanations, letters, digits, and symbols count as 1
column, Chinese characters as 2. Break lines when writing templates;
before display,
kit_code()wraps automatically withkit_code_wrap()(after commas inside parentheses, and in comments at spaces or between Chinese characters; the parse result must be the same before and after wrapping), andkit_code_cols()counts columns per line; lines still too long after wrapping (unbreakable long strings) raise a warning (they do not stop). - Code that is still long scrolls sideways inside the frame and is not wrapped.
- Don’t: open it by default; use cards, icons, or a green header; stack frames inside the frame.
Step card (StepCard)
Use step cards when a page has several steps in order: only one step is open at a time, and finished steps collapse to one line.
-
R: module
kit_stepcards_ui()/kit_stepcards_server(id, titles, summaries, checks); low levelkit_step_card(),kit_step_head(),kit_step_body()(steps 1–3 of kitJoin’s 「kits 作答檔」 (“kits response file”)).kit_steps()is the step list in the page guide, not a step card. -
Three states:
-
Current step: white background,
bw-frameframe,shadow-card; round numbernb-lime. -
Finished:
- Collapsed to one line: round ✓ in
nb-mint, followed by a one-sentence summary (for example 「5 個檔案 · 世代 TDCYP · 2009、2011(年)」 (“5 files · cohort TDCYP · 2009, 2011 (year)”)), and 「改」 (“Edit”) at the far right. - Frame
border-disabled, no shadow.
- Collapsed to one line: round ✓ in
-
Not yet available:
border-disabledframe,text-disabledtext, says 「先完成第 1 步」 (“Finish step 1 first”).
-
Current step: white background,
-
Rules:
- Move forward only by clicking 「對,下一步」 (“Yes, next step”) (a regular button); go back to a finished step with 「改」 (“Edit”).
- The primary button sits below all steps, only one; above it a 「還差:…」 (“Still needed: …”) line (see ReadyLine).
- Tables in cards have no outer frame and a pale header row; on phones each row becomes a block (see FileTable).
- The clickable area of 「改」 is at least 32px, with
aria-label「改第 N 步:標題」 (“Edit step N: title”); the current step getsaria-current="step".
- Choose first, refine later: earlier steps ask only for the minimum choices (for example step 2 only picks “which columns are items”); detailed settings are edited later in a table (for example the item table in step 3 sets domain and scoring). The explanation in the earlier step must say 「之後在第 N 步還能改」 (“You can still change this in step N”), so users do not think everything must be set at once.
- Tables to edit are open directly: when the lead of a step is a table to edit (for example the item table), show the table directly, not inside the details row; put a 「下載這張表」 (“Download this table”) secondary button next to it, so it can be downloaded at any time.
- Don’t: open several steps at once; put cards inside cards.
Checklist
The checklist lists “things to decide” together with “things that are already fine”, placed before a conversion or a run.
-
Appearance: one item per row, with a mark on the
left:
-
! (
text-decide): needs a decision. One bold sentence, one line of small text below saying why, and the decision buttons below that. -
✓ (
nb-green): fine, or already decided. Weight 500; decisions the user made say 「(你選的)」 (“(your choice)”), with 「改回來」 (“Change back”) next to it. -
i (
text-hint): reminder, no decision needed. - Thin
line-softlines between rows.
-
! (
-
Rules:
- The recommended option goes first, and its button says 「(建議)」 (“(recommended)”).
- A problem only counts as decided after a click (suspected missing-value codes, insufficient information); once decided, the result’s status box does not remind again.
- List only what is needed; the per-item table goes under 「▸ 逐題看、逐題改(45 欄)」 (“▸ Review and edit item by item (45 columns)”), 25 rows per page, searchable.
- Marks get
aria-hidden="true", preceded by hidden text 「要決定:」 (“To decide:”), 「沒問題:」 (“OK:”), 「提醒:」 (“Note:”).
- Don’t: list per-item reminders for hundreds of items.
Ready line (“what’s still needed”) (ReadyLine)
When the primary button cannot be clicked yet, one line above it says what is still needed and gives a link to fix it.
-
R:
kit_ready_line(missing, link = NULL); in the server,kit_run_gate(session, output, missing, link)also disables the button; when there are not enough files,kit_need_files(n_files, need = 2L). -
Appearance: one line of bold
text-todo「還差:…」 (“Still needed: …”), followed by annb-greenlink 「到「檔案上傳」加入檔案 →」 (“Go to ‘File upload’ to add files →”); the disabled primary button is below. -
Rules:
- Show the primary button (disabled) from the start, so people know what they will click at the end of the page.
- “Still needed” names the concrete thing and number: 「至少要 2 個波次的檔案,目前只有 1 個。」 (“Files from at least 2 waves are needed; there is only 1 now.”)
- The server must still check; do not rely only on the disabled button.
- Don’t: hide the button; repeat the same thing in a yellow callout.
Next step (NextStep)
“下一步” (“Next step”) sits at the bottom right of the page, appears only after results are out, and takes the user to the next tab.
-
R:
kit_next_step(text = NULL, button_id = NULL, label = NULL, button = NULL). -
Appearance: a
hintsentence on the left (can be a list of destinations), and a primary button 「下一步:××× →」 (“Next step: ××× →”) on the right. -
Rules:
- When it appears: the status is 【完成】 (“Done”) or 【完成,但請注意】 (“Done, but note”).
- Removed when results are outdated (【要重新執行】, “Run again”).
- The button text names the next page.
- Don’t: show it before results are out; place it in the middle of the page.
Diagram
A diagram uses a few small tables to show what the page does (before → after), placed below “這一頁做什麼” (“What this page does”).
-
R:
kit_diagram(before, after, caption, arrow = "→"), with tables fromkit_dg_table(df, title, added = NULL). -
Appearance:
- Supporting cast: no frame, tables as wide as their content,
bw-hairgrid lines, palenb-pageheader row. - Values in the
monotypeface. - Newly added columns have a
bg-newbackground and 「(新)」 (“(new)”) after the column name, so color is not the only cue. - Empty cells say 「(空)」 (“(empty)”) in gray.
- Supporting cast: no frame, tables as wide as their content,
-
Rules:
- Each small table has 2–3 rows and uses generic names (id, score, wave), not column names from a specific database.
- One
hintline of plain-language explanation below. - At ≤ 576px, “before” is on top, the arrow points down, and “after” is below.
- Don’t: use real data; use the black header row of large data tables.
File table (FileTable)
The file table is the settings table in step cards or upload pages with “one row per file, one column per setting”; on phones each row becomes a block.
-
R:
table.table.kit-file-table, withdata-label(the column name) on each<td>; used in kitJoin’s upload page, step 1 of the kits response file, and kitsUI’s item bank selection table. -
Appearance:
- Desktop: a regular table; inside step cards, no outer frame and a pale header row.
- ≤ 576px: the table and every row fill the card width; each row is a
block, each cell preceded by a
hintline with the column name, withlinedividers between rows.
-
Rules:
- Button positions are fixed in every row: unused 「上移」「下移」
(“Move up”, “Move down”) get invisible placeholders
(
btn invisible,aria-hidden), so buttons do not jump sideways. - Cells guessed from column names use
bg-guess. - Inputs are at most the layout width.
- Button positions are fixed in every row: unused 「上移」「下移」
(“Move up”, “Move down”) get invisible placeholders
(
- Don’t: let the whole page scroll sideways on phones.
Page skeleton
Every tab follows this order from top to bottom (tabs with one
primary button use
kit_run_tab_ui()/kit_run_tab_server(), which
handle the fixed parts and the state flow; the tab only writes its own
settings, run, and results):
Header (one per app)
Step tabs (current is lime, finished get ✓, not yet available are gray)
(Scope bar: an app-wide choice, only in apps that have one)
What this page does: one sentence See steps ▸
Settings (left 1/3) │ Settings summary and preview (right 2/3)
Advanced settings ▸
Still needed: … (while it cannot be clicked)
[ Primary button ]
Status box: [Done] one-sentence conclusion + at most 3 points
Key output: one table (with an h4 title) or one chart
▸ Detail 1 ▸ Detail 2 ▸ How to do it in R
[ Next step: ××× → ]
State flow
Not available ──(previous step done)──▶ Available ──(click primary button)──▶ Running
┌───────────────┬───────────────┬────────────┘
▼ ▼ ▼
[Done] [Done, but note] [Stopped]
└───────┬───────┘ (fix and click again, or click "Skip this round")
▼
"Next step" button appears; tab gets ✓
(later changes to settings or files → [Run again]; the tab's ✓ is removed, later tabs turn gray)
- After a run, scroll the status box to the top of the screen (no smooth scrolling when motion is reduced).
- Show the first successful result by default; do not preselect one that stopped.
- Actions that change results are clicked by the user; the button states the consequence clearly (「這一回合不做(題庫與連結不會改變)」, “Skip this round (item bank and linking will not change)”), and doing it leaves a log entry.
Phones (≤ 576px)
- The page must never scroll sideways:
document.documentElement.scrollWidthmust equal the window width. Inputs and tables getmax-width: 100%. - Tabs stay in one row and scroll sideways; do not wrap into several rows.
- Tables in cards become “one row per block, column name on top”
(
data-label). - The header shrinks, so the first screen shows the page guide and the first setting.
Wording
- Taiwanese Traditional Chinese, short sentences, conversational but not casual; one sentence says one thing. The subject is 「你」 (“you”) or omitted; never 「使用者」 (“the user”).
- Conclusion first, then the reason: 「這一回合資訊不足,無法估計:每個人在要估的題只剩一題。」 (“Not enough information to estimate this round: each person has only one item left to estimate.”)
- Numbers always carry units: 「688 人」 (“688 people”), 「3 個量尺」 (“3 scales”), 「2,058 欄」 (“2,058 columns”).
- Buttons start with a verb and say clearly what will happen: 「開始校準」 (“Start calibration”), 「下載 CSV」 (“Download CSV”), 「到遺漏值處理頁改掉 →」 (“Fix it on the missing value page →”); never 「確定」 (“OK”) or 「送出」 (“Submit”).
- Error messages always say what to do; R’s original message goes under 「▸ 原始訊息」 (“▸ Original message”).
- The recommended option goes first, and its button says 「(建議)」 (“(recommended)”).
- Word list (one word per concept):
| Use this | Not these |
|---|---|
| 停下來 (stopped) | 沒有完成、失敗、錯誤、中止 (not completed, failed, error, aborted) |
| 要注意 (needs attention) | 警告、warning |
| 遺漏值 (missing value) | 缺失值、missing |
| 世代 (cohort) | cohort (English may be added in details) |
| 波次 (wave) | wave (English may be added in details) |
| 這一回合不做 (skip this round) | 略過、排除、skip (skip, exclude) |
- Tab names are “noun + verb”: 檔案上傳 (file upload), 遺漏值處理 (missing value handling), 資料串接 (data join), 格式轉換 (format conversion).
- Table column names: function output and the data itself keep English column names; summary tables the app builds itself may use Chinese; downloaded files always keep the original column names.
Using it in a new Shiny app
New apps no longer copy files: just load the
kitDesign package. The theme, styles
(inst/www/*.css), browser-side scripts
(inst/www/kit.js), and logo are all in the package, and
every app uses the same copy.
# Install
remotes::install_github("jiewenTsai/kitDesign")Add kitDesign to Imports: in the app
package’s DESCRIPTION. Start the page like this:
library(kitDesign)
ver <- kit_app_description("kitJoin") # reads the app's own Version, Date
ui <- kit_page(
"KIT ×× 工具", "臺灣幼兒發展調查資料庫", # "KIT ×× tool", "KIT database"
tagline = "這個工具做什麼", # "What this tool does"
version = ver$version, date = ver$date,
kit_navgroups(groups, "tabs"), # or kit_flow_tabs(...)
shiny::tabsetPanel(id = "tabs", type = "hidden", ...)
)kit_page() already sets
lang = "zh-Hant-TW", kit_theme(), the styles
and scripts (use_kit()), and the header
(kit_header()). When you build the page yourself with
bslib::page_*(), add theme = kit_theme() and
use_kit(). To start a brand-new app:
kit_new_app(path, title, subtitle) (it includes the header,
step tabs, an example tab written to the page skeleton, and tests).
Components come in three kinds:
-
Value objects (S3):
kit_status(status box),kit_check/kit_checklist(checklist),kit_code(one version of code). They check their content when constructed (for example, the state can only be idle/success/warning/error/stale), render when placed directly in the page or passed torenderUI(), andprint()shows a text version. -
Modules (stateful components,
*_ui(id)+*_server(id)): step tabskit_step_tabs_*, step cardskit_stepcards_*, primary button + still neededkit_run_gate_*, How to do it in Rkit_code_panel_*. One line per page; no state to manage yourself. - Markup functions: all other components, plus low-level versions of the two kinds above (older apps can switch over gradually).
Which function to use for each component:
| Component | Functions |
|---|---|
| Header | Already included by kit_page(); on its own
kit_header(), kit_app_meta(); version and date
kit_app_description()
|
| Step tabs (NavTabs) | Module
kit_step_tabs_ui(id, ..., groups = )/kit_step_tabs_server(id, states, tips);
low level: kit_navgroups() + kit_navgroup(),
kit_flow_tabs(aside = ), kit_tab_states(),
kit_watch_tab_states()
|
| Scope bar (ScopeBar) |
kit_scope_bar(input, line, actions, panel); a row of
buttons in the confirmation area: .kit-buttons
|
| Page guide (PageGuide) |
kit_tab_guide(), kit_steps()
|
| Button |
kit_button(id, label, kind),
kit_btn_class()
|
| Download button (DownloadButton) |
kit_download_button(session, id, label, filename, content);
tests kit_test_download()
|
| Status box (StatusBox) | Value objects kit_status(),
kit_done_status(), kit_error_status(),
kit_stale_status() (place directly in the page); low-level
rendering kit_status_ui(); notifications
kit_notify(); plain-language errors
kit_abort()
|
| Callout |
kit_callout(..., warn = ); cloud-version banner
kit_cloud_banner(), usage notes
kit_cloud_footer(), detection
kit_is_cloud()
|
| Data table (DataTable) |
kit_static_table(), kit_preview_table(),
kit_rows_note(), kit_table_scroll(),
kit_num(), kit_dims(); interactive table
kit_dt() (DT) |
| Chart |
kit_plotly_style() (look of plotly charts),
kit_plotly_fonts(); colors kit_chart_colors(),
kit_chart_neutral(),
kit_chart_sequential()
|
| Details row (Details) |
kit_details_row(), kit_detail(),
kit_advanced(), kit_settings_line()
|
| How to do it in R (CodePanel) | Module
kit_code_panel_ui()/kit_code_panel_server();
value object kit_code(code, label); wrapping
kit_code_wrap(), line width kit_code_cols();
low level kit_code_panel(),
kit_render_code_panel(),
kit_code_blocks()
|
| Step card (StepCard) | Module
kit_stepcards_ui()/kit_stepcards_server(id, titles, summaries, checks);
low level kit_step_card(), kit_step_head(),
kit_step_body()
|
| Checklist | Value objects kit_checklist(),
kit_check(state, title, why, actions); single problems
kit_problems()
|
| Ready line (ReadyLine) | Module
kit_run_gate_ui()/kit_run_gate_server(id, missing, link);
low level kit_ready_line(), kit_run_gate(),
kit_need_files()
|
| Next step (NextStep) |
kit_next_step(); link to another app
kit_app_link()
|
| Diagram |
kit_diagram(), kit_dg_table()
|
| Scroll to results, open collapsibles, warn before leaving |
kit_scroll_to(), kit_open_details(),
kit_unload_guard()
|
| Page skeleton + state flow (RunTab) |
kit_run_tab_ui(id, run_label, guide, ...)/kit_run_tab_server(input, output, session, run_label, run, done, ...):
still needed, settings summary, run, status box, notification,
scrolling, run again, result details, How to do it in R |
- What every component looks like:
kit_gallery(). - The tool name stays “KIT ×× 工具” (“KIT ×× tool”); do not rename it when testing with other data (for example TDCYP).
- The version line is always in the header and reads Version and Date
from the app’s own DESCRIPTION (
kit_app_description()), starting with the app’s package name (package =, e.g. “kitJoin v0.6.0 · 2026-10-31”); withshow_kitdesign = TRUEthe kitDesign version follows, so both versions are visible when reporting problems. - Versioning rules and release steps: release.md; old names in kitJoin and kitsUI: migration.md.
- For new components or style changes, change kitDesign
(
inst/www/kit-design.cssandR/); do not write separate CSS in the app. Use only this system’s tokens (colors,sp-*, type sizes), and check for an existing component before writing a new one.
Switching to another brand
kitDesign defaults to KIT (lime header, KIT green, bean logo). Projects other than KIT can use it too: change the logo and colors, and follow this design language for everything else (fonts, frames, shadows, spacing, components, wording rules). From kitDesign 0.3.0:
options(kitDesign.brand = kit_brand(
"示範實驗室", # "Demo lab"
logo = "logo.svg", # full logo for the header (SVG or PNG)
mark = "mark.svg", # browser tab icon (square; falls back to logo if missing)
colors = list(accent = "#9cc0ff", primary = "#1f4fa3", page = "#f5f7fa")
))- Set it once and
kit_page(),kit_header(),kit_theme(),kit_colors(),kit_chart_sequential(), andkit_plotly_style()all use it; for a single page usekit_page(..., brand = ). - The colors you can change are the ones with a
--nb-*variable in the appendix below (see the note below the appendix “Color” table for the mapping); anything not given stays KIT. If onlyaccentis given,accent_lightis computed automatically (mixed with white); if onlyprimaryis given,primary_hoveris computed automatically (darkened). - When changing colors, keep this document’s rules: text on color
blocks is always
ink, soinkonaccentand on the four status colors needs 4.5:1;primaryis link text and also needs 4.5:1 onpage. Better not to change the four status colors; if you do, their meanings must not change. - Grays (
text-hint,line, and so on), the focus outline, and the chart categorical colors (Okabe–Ito) do not change with the brand. - To see the result:
kit_gallery(brand = "demo")(a blue demo brand).
Author’s decisions (2026-10-31)
- Tool name: stays “KIT”. TDCYP is only test data.
- Status labels: unified as 【完成】【完成,但請注意】【停下來】【要重新執行】 (“Done”, “Done, but note”, “Stopped”, “Run again”).
- Table column names: function output and the data itself keep English column names; summary tables the package builds itself may use Chinese; downloaded files keep the original column names.
- kitJoin tabs: three groups, unnumbered (準備 · 做出資料(選一個)· 其他工具; Prepare · Make the data (pick one) · Other tools).
- Table width: follows content, not stretched to the full page.
-
Text on charts: follows kits’ original (English),
not translated; labels typed by the user stay as typed (may be Chinese).
The app only applies colors and
kit_plotly_style(). -
Danger button: salmon (
nb-salmon), black text. - Heatmap color scale: the minimum is a very pale lime, not white, so 0% and no-data cells are distinguishable.
-
kits response file: step 2 only picks the item
columns; domain and scoring are set in the item table in step 3; the
default domain is the item group name with a trailing
_,., or-removed.
Where the code still differs
The sections above follow the code; check here when changing the code or the spec.
- Recommended option: v0.2 proposed a mint-background button; the code never did it. Now it is only “goes first, text says (recommended)”.
-
Secondary button: section 9 of v0.2 proposed a 2px
frame plus a 2px shadow; the code’s
kit_button(kind = "secondary")is the small button (1.5px, no shadow, 0.85rem, weight 500). The white button with a shadow is only the default look ofactionButton(). -
Type sizes: the spec has six sizes;
inst/www/kit-brite.cssstill has values such as 0.85, 0.88, 0.9, 0.92, and 1.08rem, and the first line of the status box is 1.08rem. - Weight 500: the spec says only 400 and 700, but secondary buttons and “no problem” checklist items use 500.
-
Old grays:
#4d4d4d,#6b6b6b,#9a9a9a,#c9c3b6remain in some rules ininst/www/kit-brite.css(mostly overridden by later rules); the background of not-yet-available tabs is#ece8dc, notbg-disabled.
Appendix: tokens
In code, the CSS variable is -- plus the name, for
example var(--nb-lime), var(--sp-3).
Color
| Name | Value | Used for |
|---|---|---|
nb-ink |
#141414 |
Ink: all text, 2px frame lines, hard shadows, the header background of the main data table. Text on color blocks always uses it. |
nb-page |
#faf7f0 |
Paper: the whole page background (off-white). nb-ink on it is 17.2:1. |
nb-card |
#ffffff |
Card: background of cards, inputs, tables, charts, and buttons (except the primary button). |
nb-lime |
#c6d033 |
KIT lime, the lead color: header, primary button, current tab and step number, dropdown selection. Not a text color (only 1.57:1 on paper); put nb-ink on it (10.9:1). |
nb-green |
#006835 |
KIT green (the same as the logo’s dark green): link text, progress bars, ✓ marks, selected checkboxes. 6.5:1 on paper, 6.9:1 on white. Not for large backgrounds or the primary button. |
nb-green-hover |
#004d27 |
Link color on hover. |
nb-sky |
#a8dcff |
Information: background of the sky-blue callout (kit_callout()), with nb-ink on it. |
nb-yellow |
#ffde59 |
Needs attention: 【完成,但請注意】 and 【要重新執行】 status boxes, attention callouts, the cloud-version banner. nb-ink on it. |
nb-salmon |
#ff9b8f |
Stopped: the 【停下來】 status box, danger buttons (delete, skip this round). nb-ink on it. |
nb-mint |
#b9f0a8 |
Done: the 【完成】 status box, finished step numbers, the stripes of the upload progress bar. nb-ink on it. |
text-hint |
#5a5a5a |
Supporting text: small hints, explanations, collapsible titles, group labels, secondary information in tables. 6.45:1 on paper, 6.9:1 on white. |
text-disabled |
#6e6a60 |
Disabled text: not-yet-available step cards and tabs, disabled rows. 5.0:1 on paper. |
text-todo |
#8a4a00 |
The “還差:…” (“Still needed: …”) line above the primary button, reminder text in per-item tables (bold). 6.4:1 on paper. |
text-decide |
#a14a00 |
The ! mark for “needs a decision” in checklists. About 6.0:1 on white. |
text-stop |
#8a2a00 |
Single problems in tables or lists (.kit-problems). 8.1:1 on paper. |
line |
#d9d4c5 |
Faint line: the bottom edge of “What this page does”, the bottom edge of the scope bar, the left rule of advanced settings, dividers in the phone file table. Lines only, never text. |
line-soft |
#e3ded2 |
Faintest line: between checklist rows, top edge of the composite item ID block. |
border-disabled |
#8a8a8a |
Disabled frames: not-yet-available or finished step cards, disabled buttons, not-yet-available tabs. 3.2:1 on paper (the threshold for frames and graphics is 3:1). |
bg-disabled |
#e8e4d8 |
Background of disabled buttons (do not use opacity); not-yet-available tabs use the similar #ece8dc. |
bg-stripe |
#f3f0e6 |
Zebra stripes in data tables; pale header row inside step cards. |
bg-soft-hover |
#f1eee4 |
Background of secondary buttons on hover. |
bg-code |
#fffdf5 |
Background of code frames (with a 2px nb-ink frame, no shadow). |
bg-guess |
#fffbe6 |
Cells guessed from column names (pale yellow), reminding users to check them. |
bg-new |
#e8f0a0 |
Newly added columns in diagrams, selected rows in interactive tables (pale lime); not sky blue, which means “information”. |
focus |
#0044ff |
Keyboard focus: 3px outline, offset 3px outward. Visible against black frames; 6.0:1 on paper. |
logo-bean |
#aad05f |
Logo and illustrations only. Not for buttons or backgrounds (only 1.05:1 against lime). |
logo-leaf |
#fff57f |
Logo and illustrations only. Not for any interface component (too close to the “needs attention” yellow). |
chart-blue |
#0072b2 |
Chart categorical color 1 (Okabe–Ito): cohorts, scales, and scores take colors in order. |
chart-orange |
#e69f00 |
Chart categorical color 2. |
chart-green |
#009e73 |
Chart categorical color 3. |
chart-purple |
#cc79a7 |
Chart categorical color 4. |
chart-sky |
#56b4e9 |
Chart categorical color 5. |
chart-vermillion |
#d55e00 |
Chart categorical color 6; beyond 6 categories, start over from the first. |
chart-neutral |
#7f7f7f |
Neutral color for non-categorical elements in charts: reference lines, bands, unjudged points. |
chart-sequential |
#f3f7d3 → #e8f0a0 →
#006835
|
Color scale for continuous values (heatmaps, color bars): very pale
lime (not white: white is reserved for no-data cells) → pale lime
(bg-new, at 0.2) → KIT green (nb-green),
interpolated in Lab space. kit_chart_sequential(). |
Colors a brand can change
(kit_brand(colors = ) name → token → CSS variable):
ink → nb-ink (--nb-ink),
page → nb-page (--nb-page),
card → nb-card (--nb-card),
accent → nb-lime (--nb-lime),
accent_light → bg-new
(--nb-bg-new), primary → nb-green
(--nb-green), primary_hover →
nb-green-hover (--nb-green-hover),
success → nb-mint, warning →
nb-yellow, danger → nb-salmon,
info → nb-sky (all -- plus the
name). The values in the table are KIT’s; another brand replaces these,
and chart-sequential is computed from
accent_light and primary. Other tokens do not
change with the brand.
Type sizes (base 17px)
| Name | Size | Line height | Weight | Used for |
|---|---|---|---|---|
display |
1.75rem | 1.25 | 700 | Header h1 (tool name), one per app. Shrinks to 1.35rem on phones (≤ 576px). |
h4 |
1.35rem | 1.3 | 700 | Section titles; 2.4rem above. Keep titles short; lists go in small text below. |
h5 |
1.1rem | 1.35 | 700 | Subtitles, step card titles, the primary button, the first line of the status box (implemented as 1.08rem for the status box). |
body |
1rem | 1.65 | 400 | Body text, status box bullet points. The html base size is 17px, so 1rem = 17px. |
small |
0.95rem | 1.4 | 400 | Data tables (0.95rem), step summaries and checklist explanations (0.92rem). |
hint |
0.82rem | 1.5 | 400 | Small hints, collapsible titles, group labels, the version line, diagrams. The smallest size, about 14px; anything smaller is for chart tick labels only. |
code |
0.85rem | 1.5 | 400 | “How to do it in R”, raw error messages, values in diagrams. Ligatures always off; <- and |> display as typed. |
Fonts: sans = “Chiron GoRound TC”, sans-serif;
mono = “JetBrains Mono”, “Chiron GoRound TC”, Menlo,
Consolas, monospace.
Spacing
| Name | Value | Used for |
|---|---|---|
sp-1 |
0.25rem |
Between an icon and its text. Spacing uses a 0.25rem unit. |
sp-2 |
0.5rem |
Between components in one group, such as a row of buttons or between tabs (0.5–0.6rem). |
sp-3 |
1rem |
Between components in one section, card padding, above the header. |
sp-4 |
1.6rem |
Between tab groups, from “What this page does” to the first setting, above the details row. |
sp-5 |
2.4rem |
Between sections (above h4), from the main content to “Next step”. |
gutter |
2rem |
Left and right page margins; 1rem at ≤ 768px. |
content-max |
1140px |
Content is at most 1140px wide, centered; wide screens get side margins instead of stretching to the window. |
Corners
| Name | Value | Used for |
|---|---|---|
radius |
8px |
All frames: cards, buttons, inputs, tabs, tables, charts, the header. |
radius-bar |
3px |
The upload progress bar only. |
radius-round |
50% |
The round step number in step cards (2rem square). |
Shadows
| Name | Value | Used for |
|---|---|---|
shadow-header |
4px 4px 0 0 #141414 |
The header only. |
shadow-card |
3px 3px 0 0 #141414 |
Results: status box, charts, the current step card, the primary button. |
shadow-sm |
2px 2px 0 0 #141414 |
Clickable things: tabs, regular buttons (actionButton default), dropdowns, focused inputs. |
shadow-hover |
1px 1px 0 0 #141414 |
Buttons and tabs on hover: move 1px down and to the right (2px for the primary button), leaving a 1px shadow; on press, move all the way with no shadow. |
Frame widths
| Name | Value | Used for |
|---|---|---|
bw-frame |
2px |
Frames of all top-level components: cards, buttons, inputs, tabs, table outer frames, code frames. |
bw-line |
1.5px |
Table row dividers, secondary button frames, the bottom edge of the explanation area. |
bw-hair |
1px |
Grid lines of diagram tables, the upload progress bar frame, between checklist rows. |
Breakpoints
| Name | Value | Used for |
|---|---|---|
bp-phone |
576px |
≤ 576px: smaller header, file table rows become blocks, diagrams stack vertically, main actions take priority. |
bp-tablet |
768px |
≤ 768px: 1rem side margins, tabs in one row that scrolls sideways, step card content not indented, settings and results stack vertically. |