# Referencia a exploración / Referencia a exploración

> Registrar fuente e intención → revisar → comparar tratamientos visuales.

Page: https://componentes-lovat.vercel.app/c/flow-reference/  
License: MIT · Design:   
Requires: tokens.css  
Context: herramienta · Density: media · Tone: sobria

## Files

- Tokens (once): https://componentes-lovat.vercel.app/tokens.css
- HTML: https://componentes-lovat.vercel.app/raw/flow-reference/flow-reference.html
- CSS: https://componentes-lovat.vercel.app/raw/flow-reference/flow-reference.css
- JS: https://componentes-lovat.vercel.app/raw/flow-reference/flow-reference.js
- Everything in one file: https://componentes-lovat.vercel.app/r/flow-reference.html

Dark theme: add class `dark` to `<html>`.

## When to use

- Registrar fuente e intención → revisar → comparar tratamientos visuales.

## When not to use

- Examples do not connect accounts or save files to a server.

## Accessibility

Roles: region

| Keys | Action |
|---|---|
| Tab / Enter | Navigate and activate controls |

- Errors and results use role=status.

## Code

```html
<section class="work-card" data-flow="reference"><h2>Referencia a exploración</h2><p class="steps"></p><svg data-preview viewBox="0 0 400 150" role="img" aria-label="Muestra geométrica"><rect width="400" height="150" fill="#e3d4a5"/><circle cx="120" cy="75" r="60" fill="#204e46"/><path d="M170 150 310 0 400 150" fill="#bf6447"/></svg><div data-step><label>Fuente o referencia<input data-source required placeholder="Título, autor o enlace"></label><label>Qué te interesa<textarea data-intent placeholder="Color, forma, composición…"></textarea></label><button data-next>Revisar</button></div><div data-step hidden><p>Usaremos esta muestra geométrica para comparar tratamientos. En el Canvas puedes usar tus propias imágenes en Taller gráfico.</p><button data-back>Volver</button><button data-next>Guardar referencia</button></div><div data-step hidden><p data-result role="status"></p><button data-variant="none">Original</button><button data-variant="grayscale(1)">Monocromo</button><button data-variant="hue-rotate(100deg)">Variación de color</button><button data-back>Ver comparación</button><button data-download-ref>Descargar referencia JSON</button><button data-restart>Otra referencia</button></div></section>
```

```css
.work-card{width:min(720px,100%);padding:24px;box-sizing:border-box;border:1px solid var(--border);border-radius:12px;background:var(--background);color:var(--foreground);font:14px/1.5 var(--font-sans)}.work-card h2{font-size:24px;margin:0 0 8px}.work-card label{display:grid;gap:5px;margin:12px 0}.work-card input,.work-card textarea,.work-card button{font:inherit;border:1px solid var(--border);border-radius:6px;padding:8px;background:var(--background);color:var(--foreground)}.work-card button{cursor:pointer}.work-card button:disabled{opacity:.45}.work-card :focus-visible{outline:2px solid var(--foreground);outline-offset:2px}.work-card table{width:100%;border-collapse:collapse}.work-card td,.work-card th{padding:8px 4px;text-align:left;border-bottom:1px solid var(--border)}.work-card td input{width:100%;box-sizing:border-box;min-width:65px}.work-card .table-scroll{overflow:auto}.work-card .table-scroll table{min-width:460px}.work-card .actions{display:flex;gap:8px;flex-wrap:wrap;margin:16px 0}.work-card [hidden]{display:none!important}.work-card pre{white-space:pre-wrap;max-height:240px;overflow:auto}.work-card .steps{color:var(--muted-foreground);font-size:12px}.work-card img{max-width:100%;max-height:200px;object-fit:contain}.work-card output{font-weight:600}.work-card .muted{color:var(--muted-foreground)}
```

```js
(()=>{document.querySelectorAll('[data-flow]').forEach(root=>{const panels=[...root.querySelectorAll(':scope > [data-step]')];let step=0;const show=n=>{step=n;panels.forEach((p,i)=>p.hidden=i!==n);root.querySelector('.steps').textContent=['1 · Preparar','2 · Revisar','3 · Resultado'][n];};root.querySelectorAll('[data-next]').forEach(b=>b.onclick=()=>{const input=panels[step].querySelector('input[required]');if(input&&!input.reportValidity())return;if(root.dataset.flow==='import'&&step===0&&!root.file){root.querySelector('[role=status]').textContent='Elige un archivo de texto o CSV para continuar.';return;}if(root.dataset.flow==='quote'&&step===1&&[...root.querySelectorAll('input[type=number]')].some(i=>!i.reportValidity()))return;show(Math.min(2,step+1));if(step===2){const summary=root.querySelector('[data-result]');summary.textContent=root.dataset.flow==='quote'?'Cotización revisada · USD '+root.querySelector('[data-total]').textContent:root.dataset.flow==='import'?root.file.name+' revisado. Listo para guardar una copia.':'Referencia guardada en esta demostración. Elige una variación.';}});root.querySelectorAll('[data-back]').forEach(b=>b.onclick=()=>show(Math.max(0,step-1)));root.querySelector('[data-restart]').onclick=()=>show(0);const file=root.querySelector('[data-import]');if(file)file.onchange=async e=>{const f=e.target.files[0];if(!f)return;if(f.size>1000000||!(/\.(txt|md|csv)$/i.test(f.name))){root.querySelector('[role=status]').textContent='Admite TXT, Markdown o CSV de hasta 1 MB.';root.file=null;return;}root.file=f;root.querySelector('[data-review]').textContent=await f.text();root.querySelector('[role=status]').textContent='Archivo preparado.';};const save=root.querySelector('[data-download]');if(save)save.onclick=()=>{const quote=root.querySelector('[aria-label="Costeo"]');const blob=root.dataset.flow==='import'?root.file:new Blob([JSON.stringify({cliente:root.querySelector('[data-client]').value,moneda:'USD',partidas:quote.exportRows()},null,2)],{type:'application/json'});const a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download=root.dataset.flow==='import'?root.file.name:'cotizacion.json';a.click();setTimeout(()=>URL.revokeObjectURL(a.href),1000);};root.querySelectorAll('[data-variant]').forEach(b=>b.onclick=()=>{root.querySelector('[data-preview]').style.filter=b.dataset.variant;});show(0);});})();

(()=>{const r=document.querySelector('[data-flow=reference]');r.querySelector('[data-download-ref]').onclick=()=>{const data={fuente:r.querySelector('[data-source]').value,intencion:r.querySelector('[data-intent]').value,tratamiento:r.querySelector('[data-preview]').style.filter||'none'};const a=document.createElement('a');a.href=URL.createObjectURL(new Blob([JSON.stringify(data,null,2)],{type:'application/json'}));a.download='referencia.json';a.click();setTimeout(()=>URL.revokeObjectURL(a.href),1000);};})();
```

## Credits

