정부 및 공공 서비스 · 새 리소스

Suomi.fi 디자인 시스템.

Source Sans 3, 명시적인 간격 토큰 및 복구 가능한 문서 선택 패턴으로 읽기 쉬운 핀란드 공공 서비스 양식을 만드세요.

OpenDesign 가이드8개 챕터참조 항목 16개독자적 적용 가이드
01

Suomi.fi 디자인 시스템 인터페이스 미리보기

PDF를 선택하고, 지원하지 않는 파일은 거부한 다음, 선택한 문서를 삭제해 보세요. 파일은 브라우저에만 보관됩니다.

첨부 파일을 선택하고 삭제하세요전체 예시 열기
문서화된 단일 파일 패턴을 HTML/CSS에 맞게 독자적으로 해석한 예시이며, Arial 대체 글꼴을 사용합니다. · 구현 레퍼런스
02

Suomi.fi 디자인 시스템 디자인 참고 자료

공식 문서, 확인한 토큰 저장소, suomifi-ui-components 18.0.0 메타데이터를 참고했습니다. 미리보기는 독자적으로 구현한 예시이며, 기본 React 런타임이 아닙니다.

타이포그래피

출처
이름 / 규칙값 / 구성용도
FamilySource Sans 3버전 18에서는 Source Sans 3를 사용합니다. 버전 17 이하에서는 Source Sans Pro를 사용했습니다. 글꼴은 별도로 불러오세요.
Body text18px모바일과 데스크톱에서 동일한 기본값을 사용합니다.
Small body16px자동 뷰포트 규칙이 아닌 Text의 smallScreen 처리 방식입니다.
Lead22px / 20px smallScreen화면마다 도입 문구는 하나만 사용하고, 모든 문단에 lead를 적용하지 마세요.
Weights300 / 400 / 600컴포넌트 패키지에 필요한 글꼴 파일입니다.

간격과 배경 표면

출처
이름 / 규칙값 / 구성용도
Space scale5, 10, 15, 20, 25, 30, 40, 60, 80px확인한 저장소의 JSON에 정의된 xxs부터 xxxxl까지의 값입니다.
Inset scale2, 4, 6, 8, 10, 16, 20pxinsetXxs부터 insetXxl까지의 값이며, 바깥쪽 간격 체계에는 해당하지 않습니다.
Basic radius2px저장소의 radius.basic 값입니다. 카드에 큰 모서리 반경을 임의로 적용하지 마세요.
Highlight basehsl(212, 63%, 45%)기본 상호작용 파란색입니다.
Highlight dark 1hsl(212, 63%, 37%)더 어두운 상호작용 역할 색상입니다.
Highlight light 2hsl(212, 63%, 90%)밝은 표면 역할 색상입니다. 선택한 텍스트 색상과의 대비를 확인하세요.

FileInput 레이아웃과 언어

출처
이름 / 규칙값 / 구성용도
Default maximum width600px컴포넌트별 너비이며, 사이트 전체에 적용되는 그리드가 아닙니다.
fullWidthBoolean layout option양식에서 필요하다면 부모 요소 너비에 맞게 늘리세요.
smallScreenExplicit compact presentation option이 속성만으로 전역 자동 중단점을 만들지 마세요.
Single selectionChosen file replaces picker and drop area여러 파일 선택은 별도의 패턴이며, 선택한 파일은 아래에 표시합니다.
Required copy roleslabelText, hintText, dragAreaText, removeFileText, addedFileAriaText각 역할에 맞게 현지화하세요. 파일 이름만으로는 접근성 안내를 충분히 제공할 수 없습니다.
03

Suomi.fi 디자인 시스템 전체 가이드

실제 인터페이스를 위해 이 시스템을 선택하고 적용하고 검토하는 실용 가이드.

DESIGN-suomi-fi.md

완전한 안내와 구현 시작점, 소스 링크.

가이드 다운로드

Suomi.fi 디자인 시스템 구현 범위

React 컴포넌트 시스템

Suomi.fi는 styled-components로 스타일을 적용한 React 컴포넌트와 별도의 토큰 패키지를 제공합니다. 버전 18부터 기본 글꼴이 Source Sans 3로 변경됩니다. 기존 버전 17 페이지에서는 Source Sans Pro를 계속 사용할 수 있습니다. 패키지 버전을 확인하지 않고 해당 페이지의 스크린샷을 비교하면 잘못된 타이포그래피 수정으로 이어질 수 있습니다.

이 OpenDesign 가이드에서

내장 미리보기는 문서화된 단일 파일 패턴을 HTML/CSS로 독자적으로 해석한 예시임을 명확히 표시합니다. 아래에는 검증된 콜백, 제어 값, 의존성, 글꼴 가져오기를 갖춘 별도의 기본 React FileInput 예제를 추가했습니다. 두 구현은 동일한 로컬 파일 선택 작업을 수행합니다. 기본 예제를 추가해도 내장 HTML 미리보기가 소급하여 React 컴포넌트로 바뀌는 것은 아닙니다.

지원 사양 읽기

Suomi.fi 디자인 시스템 타이포그래피와 글꼴 로딩

본문을 읽기 편하게 유지하세요

모바일과 데스크톱 모두에서 문서화된 본문 글자 크기 18px을 사용하세요. 좁은 뷰포트라고 해서 모든 양식 설명의 글자 크기를 줄여서는 안 됩니다. 작은 텍스트 스타일은 16px이며 보조 정보에 사용합니다. 도입 텍스트는 22px이고, compact 스타일에서는 20px로 줄어듭니다. 페이지 도입부에만 사용하세요.

줄바꿈을 비교하기 전에 의도한 글꼴 굵기를 불러오세요. 컴포넌트 패키지는 Source Sans 3 글꼴 파일을 사용하지만, 이 독립 미리보기에서는 의도적으로 Arial을 사용합니다. 미리보기 안내에 대체 글꼴 사용을 명시해야 하며, 기본 컴포넌트를 정확히 렌더링한 결과로 소개해서는 안 됩니다.

지원 사양 읽기

Suomi.fi 디자인 시스템 간격과 너비

서로 다른 두 가지 간격 체계

토큰 소스는 일반 간격과 내부 여백 간격을 구분합니다. 확인한 JSON에서 일반 간격 l은 25px입니다. l을 32px로 설명하는 오래된 README 예제를 그대로 사용하지 마세요. 내부 여백 간격은 2px부터 시작하며 다른 간격 체계를 따릅니다. 스타일을 생성할 때는 설치된 버전에서 실제로 내보내는 값을 사용하세요.

FileInput의 최대 너비는 문서상 600px이며 fullWidth 옵션도 제공됩니다. 이는 컴포넌트의 제약일 뿐, 모든 페이지에 적용되는 600px 너비 컨테이너의 근거는 아닙니다. 문서 제출 양식에서는 필드를 더 넓은 레이아웃 안에 배치하되 설명 문구는 레이블 가까이에 둘 수 있습니다.

지원 사양 읽기

Suomi.fi 디자인 시스템 파일 선택기 구조

문구도 컴포넌트 계약의 일부입니다

표시되는 레이블에는 요청하는 문서의 이름을 적습니다. 힌트에는 파일 선택 창을 열기 전에 지원 형식, 파일 크기 및 개수를 안내합니다. 드롭 영역은 포인터를 이용한 바로 가기를 제공하지만 파일 입력을 대체해서는 안 됩니다. 공식 가이드에 따르면 선택 컨트롤은 숨겨진 네이티브 파일 입력의 레이블로 사용합니다.

단일 파일 모드에서는 선택한 문서가 선택기와 드롭 영역을 대체합니다. 해당 문서 옆에 명확한 제거 동작을 둡니다. 여러 파일 모드에서는 목록 구성이 달라집니다. 단일 파일 요청에 추가 파일을 말없이 덧붙이지 마세요.

  • labelText: 단순히 “업로드”라고 하지 말고 요청하는 증빙 또는 문서의 이름을 적습니다.
  • removeFileText: 파일 맥락에서 제거 동작을 이해할 수 있도록 합니다.
  • addedFileAriaText: 파일명을 알리기 전에 선택 상태를 설명합니다.

지원 사양 읽기

Suomi.fi 디자인 시스템 선택 및 복구

로컬 첨부 상태 머신

이 예제에서는 최대 5,000,000바이트인 PDF 파일 하나를 허용합니다. 이 규칙은 이 워크숍 시나리오에 맞게 정한 애플리케이션 규칙입니다. 패키지 기본값이나 Suomi.fi의 공식 업로드 정책이 아닙니다. 브라우저의 accept 필터링은 파일 선택 창에 표시되는 안내일 뿐입니다. 예제에서는 선택하거나 드롭한 뒤 파일명과 크기를 별도로 확인합니다.

메타데이터가 유효하지 않으면 파일 선택기를 계속 사용할 수 있게 두고 복구 방법을 안내합니다. 파일이 유효하면 textContent를 사용해 이름을 표시하고 바이트 수를 알린 다음 포커스를 제거 컨트롤로 이동합니다. 파일을 제거하면 기본 입력값을 비우고 파일 선택기를 다시 표시한 뒤 포커스를 돌려 같은 파일을 다시 선택할 수 있게 합니다.

  1. 파일을 하나만 선택하거나 드롭합니다. 여러 파일을 선택하면 구체적인 메시지를 표시하고 거부합니다.
  2. 로컬 예제 규칙에 따라 확인한 뒤 오류 또는 선택한 파일명을 알립니다.
  3. 첨부 파일을 제거하고 원래의 선택 컨트롤을 복원합니다.

지원 사양 읽기

Suomi.fi 디자인 시스템 네이티브 통합

호환되는 의존성 세트 설치

공개된 버전 18.0.0의 FileInput 구현에서 onChange는 React change 이벤트가 아니라 FileList를 전달합니다. 제어되는 value는 파일 항목 객체의 배열입니다. 예제에서는 허용된 각 File을 { file }로 매핑합니다. 내장 제거 동작으로 목록이 바뀔 때도 같은 콜백이 실행되므로 FileList가 비어 있으면 제어 배열이 비워집니다. 이러한 계약은 공개된 dist/esm/core/Form/FileInput/FileInput.js 모듈에서 확인했습니다.

첫 렌더링부터 빈 배열로 시작해 value를 제어 상태로 유지합니다. 들어온 FileList를 배열에 매핑하기 전에 검증합니다. 이 예제에서는 이름이 .pdf로 끝나지 않거나 크기가 5,000,000바이트를 초과하는 파일을 거부하고 선택을 초기화합니다. 이는 로컬 예제 규칙이지 보안 검사나 Suomi.fi 정책이 아닙니다. 실제 업로드를 수락하기 전에 서버 측에서 콘텐츠를 검증해야 합니다.

아래 네이티브 예제는 Source Sans 3의 400 및 600 굵기를 불러오고, 명시적으로 지정한 루트 요소에 React를 마운트하며, 라이브러리를 통해 styled-components를 사용합니다. tslib를 명시적으로 포함하세요. 18.0.0 ESM 모듈은 해당 헬퍼를 가져오며, 이 의존성이 없으면 깨끗한 빌드에 실패했습니다. 아래 고정된 설정에는 누락된 런타임과 글꼴 에셋이 포함되어 있습니다. 기본 테마를 사용하는 이 예제에는 별도의 테마 공급자가 필요하지 않습니다.

지원 사양 읽기

검증된 토큰 내보내기 경로 사용

공식 스타일 가이드에서는 suomifiDesignTokens.spacing 및 suomifiDesignTokens.colors를 보여 줍니다. 아래 예제는 사용자 지정 래퍼에 해당 내보내기 경로를 사용합니다. FileInput 내부 구현을 대체하는 것은 아닙니다.

tsx
import { suomifiDesignTokens } from 'suomifi-design-tokens';
// Real exported token paths. Use values instead of copying README example numbers.
const attachmentStyle = {
  maxWidth: 600,
  padding: suomifiDesignTokens.spacing.m,
  borderColor: suomifiDesignTokens.colors.highlightBase
};

코드 참조

지원 사양 읽기

Suomi.fi 디자인 시스템 접근성 및 반응형 사용

드래그 없이도 필드 이용

드롭 영역을 눈에 띄게 표시하더라도 키보드로 조작할 수 있는 네이티브 파일 입력을 유지합니다. aria-describedby를 사용해 힌트와 오류를 연결합니다. 페이지 전체를 다시 읽지 않고도 선택 및 제거 변경 사항을 알립니다. 양식에 제거 버튼이 여러 개 있을 수 있다면 버튼이 선택한 파일을 식별하도록 합니다.

공식 smallScreen prop은 컴팩트한 표시를 제공합니다. 전역 중단점을 정의하는 것은 아닙니다. 이 독립적인 미리보기에서는 본문 텍스트를 18px로 유지하면서 애플리케이션별로 480px에서 여백을 조정합니다. 긴 파일명, 확대 상태 및 번역된 레이블을 테스트하세요. 파일명이 줄바꿈되도록 하고 전체 페이지 너비가 늘어나지 않게 합니다.

지원 사양 읽기

Suomi.fi 디자인 시스템 프로덕션 환경의 책임

파일을 선택했다고 업로드된 것은 아닙니다

데모는 문서 내용을 읽거나 요청을 보내지 않습니다. 선택한 파일명과 바이트 수만으로 문서의 유효성, 저장 여부 또는 수락 여부를 확인할 수는 없습니다. 실제 서비스에는 서버 측 검증, 업로드 진행 상태, 복구 가능한 오류 처리, 보존 규칙, 실제 응답에 기반한 확인이 필요합니다.

컴포넌트 코드는 MIT 라이선스에 따라 제공됩니다. 이 코드 라이선스가 독립 프로젝트를 핀란드 정부 서비스로 표방하거나 사용이 제한된 기관 식별 체계를 사용할 권한을 부여하지는 않습니다. 실험에는 중립적인 프로젝트 이름을 사용하고, 공식 구현에는 적절한 브랜딩 지침을 확인하세요.

지원 사양 읽기

Suomi.fi 디자인 시스템 컴포넌트 예제

공식 토큰 내보내기 경로 사용

사용자 지정 래퍼를 위한 검증된 간격 및 색상 내보내기 경로입니다.

tsx
import { suomifiDesignTokens } from 'suomifi-design-tokens';
// Real exported token paths. Use values instead of copying README example numbers.
const attachmentStyle = {
  maxWidth: 600,
  padding: suomifiDesignTokens.spacing.m,
  borderColor: suomifiDesignTokens.colors.highlightBase
};

코드 참조

단일 파일 선택 및 제거

로컬에서 완결되는 구현입니다. PDF 및 크기 제한은 시나리오에 따라 정한 값이며 업로드는 이루어지지 않습니다.

html
<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Attachment example</title><style>*{box-sizing:border-box}body{margin:0;padding:30px 20px;font:18px/1.5 Arial,sans-serif;color:#202020;background:#fff}main{max-width:600px;margin:auto}h1{font-size:32px;line-height:1.2;font-weight:400;margin:0 0 20px}p{margin:0 0 20px}label{font-weight:600;display:block}small{display:block;font-size:16px}.drop{margin-top:15px;border:2px dashed hsl(212,63%,45%);padding:30px;text-align:center;background:hsl(212,63%,90%)}button,input::file-selector-button{font:600 18px Arial;padding:12px 20px;border:1px solid hsl(212,63%,37%);border-radius:2px;background:hsl(212,63%,45%);color:white;cursor:pointer}button:focus-visible,input:focus-visible{outline:3px solid #202020;outline-offset:3px}input{width:100%;font-size:16px;margin:15px 0}input::file-selector-button{margin-right:10px}.file{border:1px solid #777;padding:20px;margin:20px 0;overflow-wrap:anywhere}.file button{margin-top:15px;background:white;color:hsl(212,63%,37%)}#error{color:#a32020;font-weight:bold}footer{font-size:14px;margin-top:30px;border-top:1px solid #ccc;padding-top:15px}[hidden]{display:none!important}@media(max-width:480px){body{padding:20px 15px}.drop{padding:20px}h1{font-size:28px}}</style><main><small>COMMUNITY WORKSHOP · ATTACHMENT</small><h1>Add your project outline</h1><p>Choose one PDF up to 5 MB. This local example checks file metadata; it does not read or send the document.</p><div id="picker"><label for="file">Project outline</label><small id="hint">One PDF file. Maximum 5 MB (5,000,000 bytes).</small><input id="file" type="file" accept=".pdf,application/pdf" aria-describedby="hint error"><div class="drop" id="drop">Or drop one PDF here</div></div><p id="error" role="alert" hidden></p><section id="selected" class="file" hidden aria-label="Selected attachment"><strong id="name"></strong><small id="size"></small><button id="remove" type="button">Remove file</button></section><p id="status" role="status">No file selected.</p><footer>OpenDesign interpretation of the Suomi.fi attachment pattern. Arial fallback; no official identity or React component runtime. PDF and 5 MB are example rules.</footer></main><script>const input=document.querySelector('#file'),picker=document.querySelector('#picker'),selected=document.querySelector('#selected'),error=document.querySelector('#error'),status=document.querySelector('#status');function choose(files){error.hidden=true;if(files.length!==1){error.textContent='Choose exactly one file.';error.hidden=false;input.value='';return}const file=files[0];if(!/\.pdf$/i.test(file.name)||file.size>5000000){error.textContent='Choose a PDF no larger than 5 MB.';error.hidden=false;input.setAttribute('aria-invalid','true');input.value='';return}input.removeAttribute('aria-invalid');document.querySelector('#name').textContent=file.name;document.querySelector('#size').textContent=file.size.toLocaleString('en')+' bytes · selected locally';document.querySelector('#remove').setAttribute('aria-label','Remove '+file.name);picker.hidden=true;selected.hidden=false;status.textContent='Selected file: '+file.name;document.querySelector('#remove').focus()}input.addEventListener('change',()=>{if(input.files.length)choose(input.files)});document.querySelector('#drop').addEventListener('dragover',e=>e.preventDefault());document.querySelector('#drop').addEventListener('drop',e=>{e.preventDefault();choose(e.dataTransfer.files)});document.querySelector('#remove').onclick=()=>{input.value='';picker.hidden=false;selected.hidden=true;error.hidden=true;input.removeAttribute('aria-invalid');status.textContent='File removed. Choose another file.';input.focus()};</script></html>

코드 참조

  • 네이티브 React 컴포넌트가 아닙니다.
  • 프로덕션 서비스에서는 서버에서 파일 내용을 검증하세요.

제어 방식의 검증을 사용하는 네이티브 FileInput

main.jsx로 저장하세요. 이 세 파일과 설정 명령을 사용하면 네이티브 React 예제를 재현할 수 있습니다.

jsx
import React, { useState } from 'react';
import { createRoot } from 'react-dom/client';
import { FileInput } from 'suomifi-ui-components';
import '@fontsource/source-sans-3/400.css';
import '@fontsource/source-sans-3/600.css';
import './app.css';
function App() {
  const [value, setValue] = useState([]);
  const [error, setError] = useState('');
  function change(files) {
    const next = Array.from(files);
    const invalid = next.length > 1 || next.some(file => !/\.pdf$/i.test(file.name) || file.size > 5000000);
    if (invalid) {
      setValue([]);
      setError('Choose one PDF no larger than 5 MB.');
      return;
    }
    setValue(next.map(file => ({ file })));
    setError('');
  }
  return <main><h1>Select a workshop document</h1><p>This local example does not upload or read the document.</p>
    <FileInput id="document" labelText="Workshop document" hintText="One PDF, maximum 5 MB (5,000,000 bytes)."
      inputButtonText="Choose PDF" dragAreaText="or drop a PDF here" removeFileText="Remove file"
      addedFileAriaText="Selected file" accept=".pdf" value={value} onChange={change}
      status={error ? 'error' : 'default'} statusText={error} />
    <p role="status">{value.length ? `${value[0].file.name} selected locally.` : 'No file selected.'}</p>
  </main>;
}
createRoot(document.getElementById('root')).render(<App />);

코드 참조

  • 애플리케이션이 유효성 검사를 처리하고, 네이티브 컴포넌트는 파일 선택과 삭제, 접근성 지원 파일 컨트롤을 제공합니다.

네이티브 예제 페이지 레이아웃

app.css로 저장하세요. 이 세 파일과 설정 명령을 사용하면 네이티브 React 예제를 그대로 재현할 수 있습니다.

CSS
body{margin:0;font:18px/1.5 'Source Sans 3',sans-serif;color:#292929;background:#fff}main{max-width:600px;margin:0 auto;padding:24px}h1{font-size:32px;line-height:1.2}*{box-sizing:border-box}

코드 참조

  • 애플리케이션이 유효성 검사를 처리하고, 네이티브 컴포넌트는 파일 선택과 삭제, 접근성 지원 파일 컨트롤을 제공합니다.

네이티브 예제 마운트 및 생성된 에셋

index.html로 저장하세요. 이 세 파일과 설정 명령을 사용하면 네이티브 React 예제를 그대로 재현할 수 있습니다.

html
<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Native Suomi.fi FileInput</title><link rel="stylesheet" href="./app.css"><div id="root"></div><script type="module" src="./app.js"></script></html>

코드 참조

  • 애플리케이션이 유효성 검사를 처리하고, 네이티브 컴포넌트는 파일 선택과 삭제, 접근성 지원 파일 컨트롤을 제공합니다.
04

Suomi.fi 디자인 시스템 AI 에이전트 설정

구현 시작점

아래 네이티브 React 예제를 빌드합니다. 로컬 Source Sans 3 글꼴 파일도 포함됩니다. 390px 및 1440px 화면에서 잘못된 형식, 유효한 파일 선택, 기본 제공 삭제 기능, 용량 제한을 초과하는 PDF를 테스트했습니다. 기존에 다운로드할 수 있는 미리보기 ZIP은 별도의 HTML/CSS 해석본이며, README는 여전히 정확합니다.

bash
npm install --save-exact [email protected] [email protected] [email protected] [email protected] [email protected] @fontsource/[email protected]
npm install --save-dev --save-exact [email protected]
# Save main.jsx, app.css and index.html from the complete examples below.
npx esbuild main.jsx --bundle --outdir=dist --entry-names=app --loader:.woff=copy --loader:.woff2=copy
cp index.html dist/index.html
python3 -m http.server 8080 --directory dist
# Open http://localhost:8080

프로젝트를 위한 프롬프트

에이전트 프롬프트
제공된 네이티브 Suomi.fi FileInput 18.0.0 React 예제를 사용하세요. Source Sans 3와 tslib를 포함해 버전을 고정한 런타임 종속성을 사용합니다. 컨트롤 값은 { file } 객체의 배열로 관리하고, onChange에서 FileList를 직접 받으세요. 해당 콜백에서 event.target을 읽지 마세요. 로컬에서 선택한 파일을 허용하기 전에 형식과 크기를 검증하고, 삭제 시 전달되는 빈 목록을 처리하며, 안내 문구와 오류 문구를 계속 표시하세요. 삽입된 HTML 미리보기는 별도의 해석본임을 명확하게 표시하세요. 어느 구현도 파일을 업로드하지 않습니다. 서버 측 검증, 진행률 표시, 저장 완료 확인에는 실제 백엔드 계약이 필요합니다.
05

Suomi.fi 디자인 시스템 출처 및 범위

삽입된 미리보기와 다운로드 가능한 ZIP은 OpenDesign HTML/CSS 해석본입니다. 별도의 완전한 React 예제는 suomifi-ui-components 18.0.0을 사용하며, FileInput의 value 및 onChange 계약을 검증했습니다. 두 예제 모두 로컬 파일 선택을 처리하지만, 어느 쪽도 서버에 문서를 업로드하거나 저장하지 않습니다.