Typography and Text Effects

City Magazine Fluid Headline Typography

Complete fluid headline typography in a city magazine scenario: includes real content, actionable controls, clear state feedback, and responsive layouts.

Typography and Text EffectsFluid Headline TypographyHTMLCSS TypographyJavaScript
With JavaScript enabled, you can edit, run, and save this example. The fixed source is as follows:
<!doctype html>
<html lang="en" dir="ltr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>City Magazine Fluid Headline Typography</title>
  <style>
    :root { --ink:#111111; --paper:#f8f8f4; --accent:#111111; --secondary:#8d8d88; --line:color-mix(in srgb, var(--ink) 18%, transparent); --shadow:0 24px 60px color-mix(in srgb, var(--ink) 16%, transparent); color-scheme:light; }
    * { box-sizing:border-box; }
    html { min-width:320px; background:var(--paper); }
    body { min-height:100vh; margin:0; overflow-x:hidden; color:var(--ink); font-family:Helvetica,"PingFang SC",sans-serif; background:repeating-linear-gradient(0deg,transparent 0 31px,color-mix(in srgb,var(--ink) 7%,transparent) 32px),var(--paper); }
    button,input,select,textarea { color:inherit; font:inherit; }
    button,[role="button"],input,select,textarea { accent-color:var(--accent); }
    button { cursor:pointer; }
    button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,[tabindex]:focus-visible { outline:3px solid var(--accent); outline-offset:3px; }
    [hidden] { display:none !important; }
    @media (max-width:700px) { .mast h1 { line-height:1.08 !important; letter-spacing:-.035em !important; overflow-wrap:anywhere; } }
    @media (prefers-reduced-motion:reduce) { *,*::before,*::after { scroll-behavior:auto !important; animation-duration:.01ms !important; animation-iteration-count:1 !important; transition-duration:.01ms !important; } }
    .page{width:min(1120px,calc(100% - 30px));margin:auto;padding:48px 0 80px}.mast{display:grid;grid-template-columns:1fr auto;border-top:10px solid var(--ink);padding-top:20px;gap:20px;align-items:end;margin-bottom:26px}.mast h1{font-family:Helvetica,"PingFang SC",sans-serif;font-weight:900;font-size:clamp(44px,10vw,106px);letter-spacing:-.075em;margin:0;line-height:.82}.eyebrow{display:inline-block;margin-bottom:12px;padding:5px 8px;color:var(--paper);background:var(--ink);font:900 10px/1 ui-monospace,monospace;letter-spacing:.14em}.intro{max-width:700px;line-height:1.55}.scenario{padding:9px 12px;border:3px solid var(--ink);font-weight:900}.demo{border:3px solid var(--ink);border-radius:0;box-shadow:12px 12px 0 var(--ink);position:relative;min-height:410px;overflow:hidden;padding:clamp(22px,4vw,46px);background:var(--paper)}@media(max-width:700px){.page{width:calc(100% - 18px);padding:20px 0}.mast{grid-template-columns:1fr}.demo{padding:15px}}
    .panel{padding:18px;border:2px solid var(--line);border-radius:26px;box-shadow:0 12px 0 color-mix(in srgb,var(--secondary) 35%,transparent)}.row{display:flex;gap:12px;justify-content:center;flex-wrap:wrap}.stack{display:grid;gap:12px}.action{padding:12px 20px;border:2px solid var(--ink);border-radius:14px;background:var(--paper);box-shadow:0 5px 0 var(--ink);font-weight:800}.action:active{transform:translateY(4px);box-shadow:0 1px 0 var(--ink)}.action.primary,.action.is-active{color:var(--paper);background:var(--accent)}.status{text-align:center;min-height:26px;margin-top:18px}.metric{text-align:center;font-size:clamp(34px,8vw,70px);font-weight:900}.muted{text-align:center;opacity:.7}.meter{height:14px;padding:2px;border:2px solid var(--ink);border-radius:999px}.meter>i{display:block;height:100%;border-radius:inherit;background:var(--secondary)}.chip{padding:7px 11px;border-radius:12px;background:color-mix(in srgb,var(--accent) 12%,transparent)}
    .composition-studio{display:grid;gap:14px}.studio-ruler{display:flex;justify-content:space-between;padding:0 4px;color:color-mix(in srgb,var(--ink) 55%,transparent);font:700 11px/1 ui-monospace,monospace;letter-spacing:.14em}.studio-ruler::before{content:"01 / WORKING PROTOTYPE"}.studio-ruler::after{content:"360 · 768 · 1440"}.composition-studio .demo::after{content:"";position:absolute;right:18px;bottom:18px;width:24px;height:24px;border-right:2px solid var(--accent);border-bottom:2px solid var(--accent);pointer-events:none}
    .fluid-type-lab{display:grid;grid-template-columns:minmax(230px,.62fr) minmax(0,1.38fr);gap:18px}.fluid-controls{display:grid;align-content:start;gap:15px}.fluid-controls label{display:grid;grid-template-columns:1fr auto;gap:8px;font-weight:800}.fluid-controls input{grid-column:1/-1;width:100%}.fluid-controls code{padding:12px;overflow:auto;border:1px dashed var(--line);border-radius:9px}.fluid-frame{display:flex;align-items:stretch;overflow:auto;padding:14px;border:1px solid var(--line);background:color-mix(in srgb,var(--secondary) 12%,transparent)}#fluidPreview{width:100%;min-width:280px;max-width:100%;padding:clamp(24px,5vw,64px);background:var(--paper)}#fluidPreview span{color:var(--accent);font-weight:900}#fluidPreview h3{max-width:15ch;margin:.25em 0;line-height:.96}#fluidPreview p{max-width:52ch;line-height:1.7}@media(max-width:720px){.fluid-type-lab{grid-template-columns:1fr}}.workflow-session{display:grid;gap:14px}.session-command{display:grid;grid-template-columns:1fr auto minmax(170px,.45fr) auto;gap:16px;align-items:end;padding:18px;border-block:4px double var(--ink)}.session-command h2{margin:.2em 0}.session-command output{font:900 clamp(28px,5vw,54px)/1 ui-monospace,monospace}.session-command label{display:grid;grid-template-columns:1fr auto;gap:7px;font-size:12px}.session-command input{grid-column:1/-1;width:100%}#workflowSessionProgress{height:12px;overflow:hidden;border-radius:2px;background:var(--line)}#workflowSessionProgress i{display:block;width:0;height:100%;background:var(--accent)}.session-stage{min-width:0;transition:filter .25s,transform .25s}.session-stage[data-session-phase="running"]{transform:translateY(-3px)}.session-stage[data-session-phase="paused"]{filter:grayscale(.65)}.session-stage[data-session-phase="complete"]{filter:contrast(.78);opacity:.72}.session-log{display:grid;grid-template-columns:auto 1fr;gap:18px;align-items:start}.session-log ol{display:flex;flex-wrap:wrap;gap:7px;padding:0;margin:0;list-style:none}.session-log li{padding:5px 8px;border:1px solid var(--line);font-size:12px}@media(max-width:900px){.session-stage{order:-1}.session-command{grid-template-columns:1fr auto}.session-command label{grid-column:1/-1}.session-log{grid-template-columns:1fr}}
  </style>
</head>
<body data-aesthetic="monochrome" data-composition="studio">
  <main class="page">
    <header class="mast"><div><span class="eyebrow">monochrome / Fluid Headline Typography / BEGINNER</span><h1>City Magazine Fluid Headline Typography</h1><p class="intro">Complete fluid headline typography in a city magazine scenario: includes real content, actionable controls, clear state feedback, and responsive layouts.</p></div><span class="scenario">City Magazine</span></header>
    <div class="composition-studio"><div class="studio-ruler" aria-hidden="true"></div><section class="demo" aria-label="City Magazine Fluid Headline Typography Interactive Area"><div class="workflow-session"><header class="session-command"><div><span class="eyebrow">City Magazine SESSION</span><h2>Magazine deadline timer</h2></div><output id="workflowSessionClock">00:45</output><label>Round Duration<input id="workflowSessionLength" type="range" min="15" max="90" step="15" value="45"><span>sec</span></label><button id="workflowSessionToggle" class="action primary" type="button">Start Copyediting</button></header><div id="workflowSessionProgress" role="progressbar" aria-label="Magazine deadline progress" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"><i></i></div><section id="workflowCore" class="workflow-core session-stage" data-session-phase="ready" tabindex="-1"><!-- 11DZ_CORE_START:typography-text-fluid-headline --><div class="fluid-type-lab"><div class="fluid-controls panel"><span class="eyebrow">FLUID HEADLINE</span><h2>Fluid Headline Typography</h2><label>Smallest font size <input id="fluidMin" type="range" min="24" max="44" value="30" data-core-bridge="typography-text-fluid-headline"><output id="fluidMinValue">30px</output></label><label>Largest size <input id="fluidMax" type="range" min="54" max="104" value="82"><output id="fluidMaxValue">82px</output></label><label>Simulated Viewport <input id="fluidViewport" type="range" min="320" max="1440" step="10" value="980"><output id="fluidViewportValue">980px</output></label><code id="fluidFormula">clamp(30px, 6vw, 82px)</code></div><div class="fluid-frame"><article id="fluidPreview" aria-label="Simulated viewport width 980 pixels"><span>City Magazine</span><h3>The headline should breathe with the space, not suddenly jump to a larger level.</h3><p>Drag the simulated viewport to observe the font size changing continuously between the upper and lower limits.</p></article></div></div><!-- 11DZ_CORE_END --></section><footer class="session-log"><strong id="workflowSessionScore">0 interaction checkpoints</strong><ol id="workflowSessionLaps" aria-label="Round Log"></ol></footer></div><p id="status" class="status" role="status" aria-live="polite"></p></section></div>
  </main>
  <script>
    'use strict';
    const statusOutput = document.querySelector('#status');
    const announce = message => { statusOutput.textContent = message; };
    /* 11DZ_CORE_SCRIPT_START:typography-text-fluid-headline */
const fluidMin=document.querySelector('#fluidMin');const fluidMax=document.querySelector('#fluidMax');const fluidViewport=document.querySelector('#fluidViewport');const fluidPreview=document.querySelector('#fluidPreview');function renderFluidType(){let min=Number(fluidMin.value);let max=Math.max(min+10,Number(fluidMax.value));if(max!==Number(fluidMax.value))fluidMax.value=max;const width=Number(fluidViewport.value);const progress=(width-320)/(1440-320);const size=min+(max-min)*Math.max(0,Math.min(1,progress));fluidPreview.style.width=Math.min(width,fluidPreview.parentElement.clientWidth)+'px';fluidPreview.querySelector('h3').style.fontSize=size.toFixed(1)+'px';fluidPreview.setAttribute('aria-label','Simulate viewport width '+width+' Pixels, headline size '+size.toFixed(1)+' pixels');document.querySelector('#fluidMinValue').textContent=min+'px';document.querySelector('#fluidMaxValue').textContent=max+'px';document.querySelector('#fluidViewportValue').textContent=width+'px';document.querySelector('#fluidFormula').textContent='clamp('+min+'px, 6vw, '+max+'px)';announce('Simulated '+width+' Pixel viewport, title '+size.toFixed(1)+' pixels');}[fluidMin,fluidMax,fluidViewport].forEach(input=>input.addEventListener('input',renderFluidType));renderFluidType();
/* 11DZ_CORE_SCRIPT_END */
const workflowBridgeSpec={"name":"typography-text-fluid-headline","selector":"[data-core-bridge=\"typography-text-fluid-headline\"]","mode":"range","key":"ArrowRight"};const workflowBridgeCore=document.querySelector('#workflowCore');const workflowBridgeControl=workflowBridgeCore.querySelector(workflowBridgeSpec.selector);if(!workflowBridgeControl)throw new Error('Missing core bridge: '+workflowBridgeSpec.name);const workflowBridgeControlledId=workflowBridgeControl.getAttribute('aria-controls');const workflowBridgeControlled=workflowBridgeControlledId?document.getElementById(workflowBridgeControlledId):null;workflowBridgeCore.dataset.workflowBridge=workflowBridgeSpec.name;workflowBridgeCore.dataset.workflowVersion='0';function workflowBridgeSerialize(){const visualNode=workflowBridgeCore.firstElementChild||workflowBridgeCore;const visual=getComputedStyle(visualNode);const outputs=[...workflowBridgeCore.querySelectorAll('output,[role="status"],[aria-live],meter,progress')].map(node=>node.textContent.trim()+'|'+node.getAttribute('aria-valuenow')+'|'+node.getAttribute('value'));const canvas=[...workflowBridgeCore.querySelectorAll('canvas')].map(node=>{try{return node.toDataURL().slice(-160);}catch{return 'canvas-unavailable';}});return JSON.stringify({html:workflowBridgeCore.innerHTML,status:statusOutput.textContent,outputs,canvas,visual:[visual.display,visual.visibility,visual.opacity,visual.transform,visual.filter,visual.backgroundColor,visual.width,visual.height],scroll:workflowBridgeSpec.mode==='scroll'?workflowBridgeControl.scrollTop:null});}let workflowBridgeLastEffect=workflowBridgeSerialize();function workflowBridgeSummary(){const label=(workflowBridgeControl.getAttribute('aria-label')||workflowBridgeControl.textContent||workflowBridgeControl.value||workflowBridgeSpec.name).trim().replace(/\s+/g,' ').slice(0,72);return label+' · Core Version '+workflowBridgeCore.dataset.workflowVersion;}function workflowBridgeCommit(){const effect=workflowBridgeSerialize();if(effect===workflowBridgeLastEffect)return {changed:false,summary:workflowBridgeSummary()};workflowBridgeLastEffect=effect;workflowBridgeCore.dataset.workflowVersion=String(Number(workflowBridgeCore.dataset.workflowVersion||0)+1);return {changed:true,summary:workflowBridgeSummary()};}function workflowBridgeScheduleCommit(){queueMicrotask(workflowBridgeCommit);setTimeout(workflowBridgeCommit,0);}workflowBridgeControl.addEventListener("input",workflowBridgeScheduleCommit);workflowBridgeControl.addEventListener('change',workflowBridgeScheduleCommit);const workflowBridgeStateTargets=new Set(workflowBridgeCore.querySelectorAll('dialog'));if(workflowBridgeControlled&&workflowBridgeCore.contains(workflowBridgeControlled))workflowBridgeStateTargets.add(workflowBridgeControlled);workflowBridgeStateTargets.forEach(target=>['close','cancel','toggle'].forEach(eventName=>target.addEventListener(eventName,workflowBridgeScheduleCommit)));function workflowBridgeActivate(){workflowBridgeControl.focus();const step=Number(workflowBridgeControl.step)||1;const min=workflowBridgeControl.min===''?-Infinity:Number(workflowBridgeControl.min);const max=workflowBridgeControl.max===''?Infinity:Number(workflowBridgeControl.max);let next=Math.min(max,Math.max(min,(Number(workflowBridgeControl.value)||0)+step));if(next===Number(workflowBridgeControl.value))next=Math.max(min,Number(workflowBridgeControl.value)-step);workflowBridgeControl.value=String(next);workflowBridgeControl.dispatchEvent(new Event('input',{bubbles:true}));workflowBridgeControl.dispatchEvent(new Event('change',{bubbles:true}));const result=workflowBridgeCommit();setTimeout(workflowBridgeCommit,0);return result;}function workflowBridgeReset(){workflowBridgeCore.dataset.workflowVersion='0';workflowBridgeLastEffect=workflowBridgeSerialize();}
const workflowSessionCore=document.querySelector('#workflowCore');const workflowSessionToggle=document.querySelector('#workflowSessionToggle');const workflowSessionLength=document.querySelector('#workflowSessionLength');const workflowSessionState={phase:'ready',elapsed:0,limit:Number(workflowSessionLength.value),checkpoints:[],timer:null};function workflowSessionFormat(seconds){return String(Math.floor(seconds/60)).padStart(2,'0')+':'+String(seconds%60).padStart(2,'0');}function workflowSessionDrive(){const percent=Math.min(100,Math.round(workflowSessionState.elapsed/workflowSessionState.limit*100));document.querySelector('#workflowSessionClock').textContent=workflowSessionFormat(Math.max(0,workflowSessionState.limit-workflowSessionState.elapsed));const progress=document.querySelector('#workflowSessionProgress');progress.setAttribute('aria-valuenow',String(percent));progress.querySelector('i').style.width=percent+'%';workflowSessionCore.dataset.sessionPhase=workflowSessionState.phase;document.querySelector('#workflowSessionScore').textContent=workflowSessionState.checkpoints.length+' interactive checkpoints';}function workflowSessionLap(label){workflowSessionState.checkpoints.push({label,at:workflowSessionState.elapsed});const item=document.createElement('li');item.textContent=label+' · '+workflowSessionFormat(workflowSessionState.elapsed);document.querySelector('#workflowSessionLaps').append(item);workflowSessionDrive();}function workflowSessionStop(complete=false){clearInterval(workflowSessionState.timer);workflowSessionState.timer=null;workflowSessionState.phase=complete?'complete':'paused';workflowSessionToggle.textContent=complete?'Restart':'Continue round';workflowSessionToggle.setAttribute('aria-pressed','false');workflowSessionDrive();announce(complete?'Magazine closing round complete':'Round paused');}function workflowSessionStart(){if(workflowSessionState.phase==='complete'){workflowSessionState.elapsed=0;workflowSessionState.checkpoints=[];document.querySelector('#workflowSessionLaps').replaceChildren();}workflowSessionState.phase='running';workflowSessionToggle.textContent='Pause round';workflowSessionToggle.setAttribute('aria-pressed','true');const bridge=workflowBridgeActivate();workflowSessionLap(bridge.summary);clearInterval(workflowSessionState.timer);workflowSessionState.timer=setInterval(()=>{workflowSessionState.elapsed+=1;if(workflowSessionState.elapsed>=workflowSessionState.limit){workflowSessionStop(true);return;}workflowSessionDrive();},1000);workflowSessionDrive();announce('Magazine deadline round has started · '+bridge.summary);}workflowSessionToggle.addEventListener('click',()=>workflowSessionState.phase==='running'?workflowSessionStop():workflowSessionStart());workflowSessionLength.addEventListener('input',()=>{workflowSessionState.limit=Number(workflowSessionLength.value);workflowSessionState.elapsed=Math.min(workflowSessionState.elapsed,workflowSessionState.limit);workflowSessionDrive();});workflowSessionCore.addEventListener('keydown',event=>{if(event.key==='Enter'||event.key===' '){workflowSessionLap('Keyboard Interaction');}});workflowSessionCore.addEventListener('click',event=>{if(event.target.closest('[data-core-bridge]'))workflowSessionLap('Core action · '+workflowBridgeSummary());});document.addEventListener('visibilitychange',()=>{if(document.hidden&&workflowSessionState.phase==='running')workflowSessionStop();});workflowSessionDrive();
workflowBridgeReset();
  </script>
</body>
</html>
Page feedbackFound an issue or have suggestions for improvement?
Feedback features need to be used in the online version

Please enable JavaScript in the online version before submitting; local features of the current tool or example are not affected.

Go to the online version for feedback