import React, { useEffect, useState } from 'react'
import { createRoot } from 'react-dom/client'
import './styles.css'
const navItems = ['graphic', 'fashion', 'culture', 'journal', 'contact']
function Header({ page, onNavigate }) {
const [open, setOpen] = useState(false)
return
onNavigate('home')}>TINTORY
setOpen(!open)} aria-label="menu">{open ? 'CLOSE' : 'MENU'}
{navItems.map(item => { onNavigate(item); setOpen(false) }}>{item} )}
}
const Kicker = ({children}) =>
{children}
const Arrow = () => ↗
function NotePile({ onNavigate }) {
const notes = [
['FILE 01 / CULTURE', 'Silver as\nAncestral Light', '苗族银饰如何成为家族、迁徙与祝福的视觉语言。', 'culture'],
['FILE 02 / FASHION', 'Lightness as\nResistance', 'Chanel SS26 Finds Air Within Tweed.', 'fashion'],
['FILE 03 / FASHION', 'Proportion Speaks\nBefore Color', '廓形先于色彩,而不是装饰。', 'fashion'],
['FILE 04 / GRAPHIC', 'Visual\nGrammar', 'Typography. Grid and Rhythm.', 'graphic'],
['FILE 05 / JOURNAL', 'Fragments\nof Time', 'Images as traces of memory.', 'journal'],
]
return
{notes.map((n, i) => onNavigate(n[3])}>
{n[0]} {n[1].split('\n').map((s,j)=>{s}{j===0&& } )} {n[2]} FIND: {n[3]} PAGE
)}
}
function Home({ onNavigate }) {
return
Tintory Studio
一个以视觉考古为方法的研究型工作室,在平面设计、时尚观察与文化探索之间,寻找被时间、身体与地域共同塑造的美学秩序。
Visual Archaeology Established 2026
GRAPHIC FASHION CULTURE
01 / MANIFESTO
A VISUAL RESEARCH STUDIO 我们不制造风格。 我们发掘结构。 Tintory 将图像视为一种历史材料,穿梭在字体、服装、地方与记忆之间。我们关心的不是表面,而是形式如何被生活慢慢塑成。
}
function Graphic() {
return
GRAPHIC
Shape beforedecoration. 文字、比例与留白不是装饰,而是信息被看见的方式。我们把每一次设计都当作一次结构研究。
IDENTITY EDITORIAL TYPE SYSTEM ART DIRECTION
01 / IDENTITY +
Visual systems made to move. 让标识、文字与网格拥有可持续生长的秩序。
02 / EDITORIAL Aa
The Architecture of Typography 字体的尺度,就是内容的语气。
03 / POSTER
Posters as Memory 图像如何保存事件发生过的痕迹。
}
function Fashion() {
const items = [
['01', 'Lightness as Resistance', '轻盈不是纤弱,而是一种关于身体自由的设计立场。'],
['02', 'Body as Architecture', '廓形组织身体,也组织人在空间中的存在方式。'],
['03', 'The Silence of a Black Coat', '黑色、沉默与步伐建立一场关于身体边界的论证。'],
['04', 'Figures Made of Light and Skin', '皮影戏借助幕布、雕刻与光线,获得流动的新身体。'],
]
return
FASHION
Body as Architecture 我们观察服装如何塑造身体,秀场如何组织空间,品牌如何书写时代,而风格如何成为一个人的视觉传记。
RUNWAY秀场报道 BRAND ARCHIVE品牌故事 STYLING风格分析 PROFILES人物传记
III / ARCHIVE
Notes, essays and visual readings.
{items.map(item => {item[0]} {item[1]} {item[2]}
)}
}
const cultureCards = [
['/assets/miao.jpg','SOUTHWEST CHINA / MIAO','Silver as Ancestral Light','银饰不仅被佩戴在身体之上,也记录家族、迁徙与祝福。'],
['/assets/tibetan.jpg','TIBETAN PLATEAU / TIBETAN','Colour at the Edge of the Sky','红、白、蓝、黄与天空、土地和建筑共同构成生活的视觉秩序。'],
['/assets/dong.jpg','GUIZHOU / DONG','Architecture Without a Nail','榫卯将木材连接成鼓楼、风雨桥与居所。'],
]
function Culture() {
return
I / ETHNIC AESTHETICS
REGIONAL VISUAL LANGUAGE
Patterns carry memory before words do.
按地域与民族进入视觉传统:服饰、建筑、纹样、材料与色彩并非装饰性的表面,而是一个群体理解自然、身份与时间的方式。
{cultureCards.map((card, i) => {card[1]} {card[2]} {card[3]}
)}
III / CULTURAL STORIES LONG-FORM NARRATIVES
A tradition survives each time it is retold. 通过长篇叙事进入神话、民俗与工艺传承。这里关心的不只是“过去发生了什么”,更是故事如何在口述、手艺与仪式中继续改变今天。
}
function Bookshelf() {
return
ON PHOTOGRAPHY
WAYS OF SEEING METHOD How vision is shaped by culture.
READ →
CAMERA LUCIDA
The Image
}
function Journal() {
return
III / ARCHIVE ARCHIVE LIST Notes, essays and visual readings. ALL RUNWAY BRAND ARCHIVE STYLING PROFILES
{['The Silence of a Black Coat','Miu Miu and the Grammar of Youth','Why Minimalism Feels Powerful'].map((x,i)=>
2026 / {i?'BRAND ARCHIVE':'RUNWAY'} {x} 一则关于视觉、身体与时代语法的观察笔记。
)}
}
function Contact() {
return
01 / INTRO STUDIO COLLABORATION Let's create something meaningful. Hi, I'm Mia.
I am a photographer and designer exploring the relationship between images, culture and visual narratives.
Through photography and visual design, I create emotional experiences grounded in research.
}
const pages = { home: Home, graphic: Graphic, fashion: Fashion, culture: Culture, journal: Journal, contact: Contact }
function App() {
const getPage = () => window.location.hash.replace('#/','') || 'home'
const [page, setPage] = useState(getPage)
useEffect(() => { const sync = () => setPage(getPage()); addEventListener('hashchange', sync); return () => removeEventListener('hashchange', sync) }, [])
const navigate = next => { window.location.hash = next === 'home' ? '/' : `/${next}`; setPage(next); window.scrollTo({top:0,behavior:'smooth'}) }
const Page = pages[page] || Home
return <> scrollTo({top:0,behavior:'smooth'})}>↑ >
}
createRoot(document.getElementById('root')).render( )