Add 150 UI and page state stickers in three pending sections

404s, loaders, success, errors, empty states, confirmation, under
construction, offline and more: 50 monochrome (ui), 50 colored (uicolor)
and 50 with an anime girl (uianime), on /test until approved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 03:10:04 +00:00
co-authored by Claude Opus 5.5
parent c59222f593
commit c00ba79178
450 changed files with 5481 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
// rounded progress bar: the striped fill eases out to full while its stripes slide, then slips off the end and wraps round to start again
const{min,max}=Math
export const frames=24,delay=70
const io=u=>(u=min(1,max(0,u)),u<.5?2*u*u:1-2*(1-u)**2)
export default(c,t)=>{
const X=32,W=192,b=X+W*io(t/.7),a=X+W*io((t-.72)/.26)
c.beginPath();c.roundRect(18,92,220,72,36);c.fill();c.stroke()
if(b-a<1)return
c.save();c.beginPath();c.roundRect(32,106,192,44,22);c.clip()
c.beginPath();c.roundRect(a,106,b-a,44,22);c.clip()
c.fillStyle='#111';c.fillRect(0,0,256,256)
c.fillStyle='#888';const o=t*72%24
for(let x=-48+o;x<256;x+=24)c.beginPath(),c.moveTo(x,150),c.lineTo(x+12,150),c.lineTo(x+34,106),c.lineTo(x+22,106),c.fill()
c.restore()
}
+18
View File
@@ -0,0 +1,18 @@
// sliding puzzle loader: rounded blocks in a 2x2 tray take turns sliding into the one empty slot, so the gap walks round and round
const{min,max}=Math
export const frames=24,delay=70
const S=[[44,44],[132,44],[132,132],[44,132]],io=u=>(u=min(1,max(0,u)),u<.5?2*u*u:1-2*(1-u)**2)
export default(c,t)=>{
const n=Math.floor(t*4),p=io((t*4%1)/.6),g=(7-n)%4
c.beginPath();c.roundRect(28,28,200,200,28);c.fill();c.stroke()
c.fillStyle='#ddd';for(const[x,y]of S)c.beginPath(),c.roundRect(x+6,y+6,68,68,14),c.fill()
c.fillStyle='#fff'
for(let k=0;k<4;k++){
if(k==g)continue
const m=k==(g+3)%4,[x0,y0]=S[k],[x1,y1]=S[g],x=m?x0+(x1-x0)*p:x0,y=m?y0+(y1-y0)*p:y0
c.beginPath();c.roundRect(x,y,80,80,18);c.fill();c.stroke()
const dx=m&&p<1?(x1-x0)/88*6:0,dy=m&&p<1?(y1-y0)/88*6:0
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(x+40+d*13+dx,y+38+dy,5,0,2*Math.PI),c.fill()
c.fillStyle='#fff'
}
}
+12
View File
@@ -0,0 +1,12 @@
// three chunky dots hop up one after another in a wave, stretching as they fly and squashing as they land
const{sin,cos,PI,max,abs}=Math,T=2*PI
export const frames=20,delay=60
export default(c,t)=>{
for(let k=0;k<3;k++){
const x=54+k*74,u=((t-k*.13)%1+1)%1,h=u<.5?sin(T*u):0,v=u<.5?abs(sin(2*T*u)):0,q=u>.5&&u<.62?sin(PI*(u-.5)/.12):0
const sx=1-.05*v+.16*q,sy=1+.07*v-.18*q,r=31,y=196-80*h
c.fillStyle='#bbb';c.beginPath();c.ellipse(x,200,24*(1-.45*h)*sx,5*(1-.4*h),0,0,T);c.fill()
c.fillStyle='#fff';c.save();c.translate(x,y);c.scale(sx,sy)
c.lineWidth=7/((sx+sy)/2);c.beginPath();c.arc(0,-r,r,0,T);c.fill();c.stroke();c.restore()
}
}
+20
View File
@@ -0,0 +1,20 @@
// a little smiling planet while two moons circle it at different speeds along dashed orbit paths
const{sin,cos,PI}=Math,T=2*PI
export const frames=24,delay=70
const O=[[72,38,-.42,2,12],[102,58,.3,1,15]]
export default(c,t)=>{
const pt=([rx,ry,a],f)=>[128+rx*cos(f)*cos(a)-ry*sin(f)*sin(a),128+rx*cos(f)*sin(a)+ry*sin(f)*cos(a)]
const orb=(o,a0,a1)=>{for(const[w,s]of[[9,'#111'],[3.5,'#fff']]){c.lineWidth=w;c.strokeStyle=s;c.setLineDash([9,18]);c.beginPath();c.ellipse(128,128,o[0],o[1],o[2],a0,a1);c.stroke()}c.setLineDash([])}
const M=O.map(o=>{const f=o[3]*T*t+o[3];return[...pt(o,f),o[4]*(1+.15*sin(f)),sin(f)]})
const moon=([x,y,r])=>{c.lineWidth=6;c.strokeStyle='#111';c.beginPath();c.arc(x,y,r,0,T);c.fill();c.stroke();c.fillStyle='#ccc';c.beginPath();c.arc(x+r*.3,y-r*.25,r*.28,0,T);c.fill();c.fillStyle='#fff'}
for(const o of O)orb(o,PI,T)
for(const m of M)if(m[3]<0)moon(m)
const b=3*sin(T*t*2)
c.lineWidth=7;c.strokeStyle='#111';c.beginPath();c.arc(128,128+b,40,0,T);c.fill()
c.save();c.clip();c.fillStyle='#ddd';c.beginPath();c.ellipse(128,152+b,52,10,-.12,0,T);c.fill();c.restore()
c.beginPath();c.arc(128,128+b,40,0,T);c.stroke()
c.fillStyle='#111';for(const d of[-1,1])c.beginPath(),c.arc(128+d*12,122+b,4.5,0,T),c.fill()
c.lineWidth=4;c.beginPath();c.arc(128,130+b,7,.15*PI,.85*PI);c.stroke();c.fillStyle='#fff'
for(const o of O)orb(o,0,PI)
for(const m of M)if(m[3]>=0)moon(m)
}
+12
View File
@@ -0,0 +1,12 @@
// a dot sends out radar ripple rings that grow wider and thinner until they fade away
const{cos,PI}=Math,T=2*PI
export const frames=15,delay=80
export default(c,t)=>{
for(let k=0;k<3;k++){
const p=(t+k/3)%1,r=28+88*p
c.lineWidth=16*(1-p)**.7;c.strokeStyle='#999';c.beginPath();c.arc(128,128,r,0,T);c.stroke()
}
const s=1+.08*cos(T*3*t)
c.lineWidth=7;c.strokeStyle='#111';c.beginPath();c.arc(128,128,30*s,0,T);c.fill();c.stroke()
c.fillStyle='#111';c.beginPath();c.arc(128,128,13*s,0,T);c.fill()
}
+8
View File
@@ -0,0 +1,8 @@
// material style spinner: one thick arc spins round, stretching long and then shrinking short as it goes
const{PI,min,max,floor}=Math,T=2*PI
export const frames=30,delay=50
const e=u=>(u=min(1,max(0,u)),u<.5?4*u**3:1-4*(1-u)**3)
export default(c,t)=>{
const q=t*2%1,G=1.5*PI,o=T*t*1.5+floor(t*2)*G-PI/2,a=o+G*e(q*2-1),b=o+.6+G*e(q*2)
for(const[w,s]of[[42,'#111'],[28,'#fff']])c.lineWidth=w,c.strokeStyle=s,c.beginPath(),c.arc(128,128,86,a,b),c.stroke()
}
+13
View File
@@ -0,0 +1,13 @@
// skeleton placeholder card: gray avatar, text lines and image box with a soft shimmer band sweeping across them
const T=2*Math.PI
export const frames=20,delay=70
export default(c,t)=>{
c.beginPath();c.roundRect(22,22,212,212,22);c.fill();c.stroke()
const x=-70+t*400,g=c.createLinearGradient(x-56,150,x+56,106)
for(const[o,s]of[[0,'#d6d6d6'],[.5,'#f4f4f4'],[1,'#d6d6d6']])g.addColorStop(o,s)
c.fillStyle=g;c.beginPath();c.arc(68,68,24,0,T)
for(const[x,y,w]of[[104,52,104],[104,74,70],[42,188,172],[42,208,116]])c.roundRect(x,y,w,13,6.5)
c.roundRect(42,106,172,70,12);c.fill()
c.fillStyle='#c4c4c4';c.beginPath();c.moveTo(96,166);c.lineTo(122,132);c.lineTo(140,152);c.lineTo(152,140);c.lineTo(172,166);c.fill()
c.beginPath();c.arc(170,128,9,0,T);c.fill()
}