About
A file lifts into progress and confirmation. Includes White, Black and Blue palettes and an interactive sample-file demo.
Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Matte Upload to your project and fits it to your theme.
Code
How Matte Upload is used: the demo's code. The component's own source comes with a free account.
Usage.tsx
import {useState as useTransferState,useEffect as useTransferEffect,useRef as useTransferRef} from 'react';
'use client';
import {useState} from 'react';
import Component from '@/components/ui/matte-upload';
// Demo only. Replace handlers with your real application operations.
function wait(signal){return new Promise((resolve,reject)=>{if(signal?.aborted){reject(new DOMException('Canceled','AbortError'));return;}const timer=setTimeout(()=>{signal?.removeEventListener('abort',abort);resolve();},700);function abort(){clearTimeout(timer);reject(new DOMException('Canceled','AbortError'));}signal?.addEventListener('abort',abort,{once:true});});}
const defaults={palette:"white",presentation:"upload"};
const presentations={"upload":"Original","upload-text":"Text","upload-icon":"Icon"};
export default function Demo(props={}){
const [transferPalette,setTransferPalette]=useTransferState(defaults.palette),[transferPresentation,setTransferPresentation]=useTransferState(defaults.presentation);
useTransferEffect(()=>{if(['white','black','blue'].includes(props.palette))setTransferPalette(props.palette);if(Object.hasOwn(presentations,props.presentation))setTransferPresentation(props.presentation);},[props.palette,props.presentation]);
const s={...defaults,...props,palette:transferPalette,presentation:transferPresentation};
const [message,setMessage]=useState("Demo upload. No files are sent.");
return <div style={{width:"100%",minHeight:360,padding:"clamp(12px, 4vw, 32px)",boxSizing:"border-box",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",gap:24,color:"var(--color-foreground)"}}><DemoSurface palette={s.palette} presentation={s.presentation} callbacks={{onUpload:simulateUpload}} onError={error=>setMessage(error.message)}/><p role="status" style={{margin:0,maxWidth:380,width:"100%",minHeight:"3lh",textAlign:"center",fontSize:12,lineHeight:1.5,color:"var(--color-muted-foreground)"}}>{message}</p><div aria-label="Demo options" style={{display:'flex',alignItems:'center',justifyContent:'center',gap:16,flexWrap:'wrap'}}><div role="group" aria-label="Palette" style={{display:'flex',gap:8}}>{[['white','#fafbfd'],['black','#292d33'],['blue','#55749a']].map(([value,color])=><button key={value} type="button" aria-label={value[0].toUpperCase()+value.slice(1)+' palette'} aria-pressed={s.palette===value} onClick={()=>setTransferPalette(value)} style={{width:28,height:28,padding:3,borderRadius:'50%',border:s.palette===value?'2px solid var(--color-primary)':'1px solid var(--color-border)',background:color,backgroundClip:'content-box',cursor:'pointer'}}/>)}</div>{Object.keys(presentations).length>1&&<select aria-label="Presentation" value={s.presentation} onChange={e=>setTransferPresentation(e.target.value)} style={{font:'inherit',fontSize:12,padding:'8px 12px',borderRadius:9,border:'1px solid var(--color-border)',background:'var(--color-background)',color:'var(--color-foreground)'}}>{Object.entries(presentations).map(([value,label])=><option key={value} value={value}>{label}</option>)}</select>}</div></div>;
}
function DemoSurface(props){
const surface=useTransferRef(null);
const demoController=useTransferRef(null);
function playDemo(event){
const isUpload=event.nativeEvent.composedPath().some(node=>node?.matches?.('.sl-upload'));
if(!isUpload)return;
event.preventDefault();
event.stopPropagation();
const controller=demoController.current;
if(!controller||controller.state==='pending')return;
void controller.select(new File(['SL UI Library sample'],'demo-file.txt',{type:'text/plain'}));
}
function onReady(controller){
demoController.current=controller;
const root=surface.current?.querySelector('[data-sl-react] > div:first-child')?.shadowRoot;
if(root&&!root.querySelector('[data-sl-demo-layout]')){
const css=document.createElement('style');
css.setAttribute('data-sl-demo-layout','');
css.textContent=':host{display:block!important;min-inline-size:0} :host > button{margin-inline:auto} :host > p[role="status"]{margin:10px 0 0;min-block-size:4lh;text-align:center;overflow-wrap:anywhere}';
root.append(css);
}
const feedback=root?.querySelector('.upload-feedback');
if(feedback)feedback.textContent='Click Upload to try a sample file.';
props.onReady?.(controller);
}
return <div ref={surface} onClickCapture={playDemo} data-sl-demo-surface style={{width:'min(100%, 400px)',minHeight:144,padding:'24px clamp(12px, 4vw, 24px) 12px',borderRadius:16,background:'#f7f9fb',color:'#364152',boxSizing:'border-box'}}><Component {...props} onReady={onReady}/></div>;
}
async function simulateUpload(_file,{signal,onProgress}){
for(const progress of [0.25,0.5,0.75,1]){
await wait(signal);
onProgress(progress);
}
}















