TYPOGRAPHY / 258
Typographic image
文字の密度で絵をつくる
同じ文字を敷き詰め、文字の内側へ明暗の模様を映します。
見本の中を操作して変化を試してください。
実装コードを見る
<!doctype html><html lang="ja"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>body{margin:0;min-height:330px}.sample{width:100%;height:100%;min-height:330px;padding:12px!important;display:flex!important;flex-direction:column;justify-content:center;align-items:center;gap:8px!important;background:#f0efeb;color:#232323;font-family:Arial,Meiryo,sans-serif;overflow:hidden;box-sizing:border-box}.sample *{box-sizing:border-box}.lab-scene{width:100%;max-width:430px;height:140px;position:relative;overflow:hidden;border-bottom:1px solid #ccc}.lab-scene:has(canvas){max-width:320px;height:auto;aspect-ratio:16/9;flex-shrink:0}.lab-canvas{display:block;width:100%;height:100%!important;outline-offset:-3px}.lab-canvas:focus-visible{outline:2px solid #222}.lab-controls{width:100%;max-width:390px;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 14px}.lab-controls label{display:grid;grid-template-columns:1fr auto;gap:2px;font-size:11px;min-width:0}.lab-controls input{grid-column:1/-1;width:100%;min-width:0;margin:0;accent-color:#333;height:30px}.lab-controls output{font-variant-numeric:tabular-nums}.lab-actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap;justify-content:center}.sample .lab-actions button{color:inherit;font:11px Arial,Meiryo,sans-serif;border:1px solid #aaa;border-radius:99px;background:transparent;min-height:44px;padding:8px 12px;cursor:pointer}.lab-status{font-size:10px;line-height:1.4;min-height:14px;margin:0;text-align:center}.lab-note{font-size:9px;line-height:1.3;color:#666;margin:0;text-align:center}.lab-actions button:focus-visible{outline:2px solid #222;outline-offset:2px}@media(prefers-reduced-motion:reduce){.sample *{animation-play-state:paused!important;transition:none!important}}.lab-scene{display:grid;place-items:center}.editorial-word{font:bold 58px Arial;letter-spacing:-4px;display:flex;align-items:center;justify-content:center;width:100%;height:100%;text-align:center}.editorial-word i{font-style:normal;display:inline-block}.editorial-word small{font-size:12px;letter-spacing:.1em}.type-picture{width:100%;height:140px;overflow:hidden;font:bold calc(var(--size,12)*1px)/1 monospace;word-break:break-all;background:radial-gradient(ellipse at 45% 45%,#ddd 0 24%,#222 25% 38%,#bbb 39% 50%,#111 51%);background-clip:text;-webkit-background-clip:text;color:transparent}.changed .type-picture{background-size:140% 140%}</style><div class="sample"><div class="lab-scene"><div class="type-picture">GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION GOOD EXPRESSION </div></div><div class="lab-controls"><label>文字の大きさ px<output>12</output><input aria-label="文字の大きさ px" type="range" min="7" max="22" step="1" value="12" data-key="size"></label></div><div class="lab-actions"><button data-play>停止</button><button data-reset>構図を変える</button></div><p class="lab-status" role="status"></p></div><script>const lab=(function runDomLab(root,cfg,setup){
const abort=new AbortController(),values=Object.fromEntries(cfg.controls.map(c=>[c.key,c.value])),mq=matchMedia('(prefers-reduced-motion:reduce)');let auto=!mq.matches;
const on=(el,event,fn)=>el.addEventListener(event,fn,{signal:abort.signal});
const api=setup({root,values,kind:cfg.kind,on,reduced:mq.matches})||{};
for(const input of root.querySelectorAll('[data-key]'))on(input,'input',()=>{values[input.dataset.key]=Number(input.value);input.closest('label').querySelector('output').textContent=input.value;api.update?.()});
const play=root.querySelector('[data-play]');on(play,'click',()=>{auto=!auto;api.setAuto?.(auto);for(const a of root.querySelector('.lab-scene').getAnimations({subtree:true}))auto?a.play():a.pause();play.textContent=auto?'停止':'再生';play.setAttribute('aria-pressed',String(!auto))});
const reset=root.querySelector('[data-reset]');on(reset,'click',()=>api.action?.());
on(document,'visibilitychange',()=>api.setAuto?.(auto&&!document.hidden));
api.update?.();if(cfg.static)play.hidden=true;
return {setAuto(on){auto=on&&!mq.matches;api.setAuto?.(auto);for(const a of root.querySelector('.lab-scene').getAnimations({subtree:true}))auto?a.play():a.pause();play.textContent=auto?'停止':'再生'},destroy(){abort.abort();api.destroy?.()}};
})(document,{"kind":"type-picture","controls":[{"key":"size","label":"文字の大きさ px","min":7,"max":22,"value":12,"step":1}],"static":true,"action":"構図を変える"},function editorial({root,values:v,kind}){const stage=root.querySelector('.lab-scene'),status=root.querySelector('.lab-status');let changed=false;return {update(){for(const[k,x]of Object.entries(v))stage.style.setProperty('--'+k,String(x));if(kind==='type-variable'){root.querySelector('.editorial-word').style.fontVariationSettings=`"wght" ${v.weight}`;}if(kind==='texture-grain')root.querySelector('feTurbulence').setAttribute('baseFrequency',String(v.frequency));if(kind==='layout-intrinsic')root.querySelector('.layout-board').style.width=v.width+'%';status.textContent=kind==='type-variable'?'Inter variable / wght '+v.weight:'';},action(){changed=!changed;stage.classList.toggle('changed',changed);if(kind==='type-variable'){root.querySelector('.editorial-word').style.fontVariationSettings=`"wght" ${changed?400:v.weight}`;status.textContent=changed?'比較 / wght 400':'選んだ太さ / wght '+v.weight;}else status.textContent=changed?'変化後':'元の状態';}}});addEventListener('pagehide',()=>lab.destroy(),{once:true});</script></html>