An Introduction to css-doodle
Yuan Chuan
How it begins
<div class="grid"> <div class="cell"> <div class="cell"> <div class="cell"> <div class="cell"> <!-- ... --> </div> <style> .cell:nth-child(1) { /* ... */ } .cell:nth-child(2) { /* ... */ } .cell:nth-child(3) { /* ... */ } .cell:nth-child(4) { /* ... */ } <!-- ... --> </style>
<x-pattern> _.backgroundColor = color(); _.borderRadius = rand( '100% 0 0 0', '0 100% 0 0', '0 0 100% 0', '0 0 0 100%' ); </x-pattern>
<css-doodle> @grid: 6x5 / 37vw auto / #f5f5f5; background: @p( #252386, #C92995, #91228C, #4C1981 ); border-radius: @p.cycle( 100% 0 0 0 ); </css-doodle>
<css-doodle> @grid: 6x5 / 37vw auto / #f5f5f5; background: @p( #252386, #C92995, #91228C, #4C1981 ); clip-path: circle( 100% at @M2.p(0, 100%) ); </css-doodle>
<css-doodle> @grid: 6x5 / 37vw auto / #f5f5f5; background: radial-gradient( @p(#252386, #C92995, #91228C, #4C1981) 70.7%, #0000 0 ); background-size: 200% 200%; background-position: @M2.p(-100%, 0); </css-doodle>
css-doodle doesn't have a real
CSS parser
selector { property: value; }
Selector
@col {}
@row {}
@at {}
@nth {}
@even {}
@odd {}
@random {}
@match {}
@hover {}
Property
@grid
@seed
@gap
@size
@place
@use
@shape
@content
Value
@i, @I, @x, @y, @X, @Y
@m, @M, @n, @nx, @ny, @N
@p, @P, @pl, @pr, @pd, @lp,
@r, @rn, @lr,
@svg, @svg-polygon,
@doodle, @shaders, @pattern
@mirror, @cycle, @reverse,
@plot, @shape
...
@grid: 5 / 32vw; @content: @i; @odd { background: #000; color: #fff; } @nth(13) { background: red; @shape: star; }
@grid: 10 / 32vw no-clip; background: #000; margin: auto; @size: calc(@i * 1%); rotate: calc(360deg / @I * @i);
@grid: 10 / 32vw no-clip; background: #000; margin: auto;@size: calc(@i * 1%);
@size: @i(*1%);rotate: calc(360deg / @I * @i);
rotate: @iI(*360deg);
@size: calc(@i * 1%);
@size: @i(*1%);
rotate: calc(360deg / @I * @i);
rotate: @iI(*360deg);
Function syntax
@i
@i()
@i(5)
@i(*10)
@i(10/)
@i(*10, -2, 48/)
calc(48 / (@i * 10 - 2))
@r
@r(1)
@r(0, 1)
@r255
@r.@r255
@r.r.r.r.r255
@r(@r(@r(@r(@r(255)))))
@grid: 2000x1 / 32vw; @place: center; @size: 4px; border-radius: 50%; background: #000; --d: 1 - @r.r.r.r; translate: $vw(15d * @cos.iI(*2π)) $vw(15d * @sin.iI(*2π))
Random functions
@pick, @p @P @pl, @pn @pr, @pnr @pd
@p(1,2,3,4,5)
@p([1-5])
@P([1-5])
@pd([1-5])
@pl([1-5]) / @pn([1-5])
@pr([1-5])
@grid: 3x6 / 50vw auto; --mode: @pl( normal, multiply, screen, overlay, darken, lighten, color-dodge, color-burn, hard-light, soft-light, difference, exclusion, hue, saturation, color, luminosity, plus-darker, plus-lighter ); background: @doodle( @grid: 3x1; mix-blend-mode: @p(--mode); border-radius: 50%; background: @pl(red, blue, yellow); @place: @plot(r: .25); @size: 40%; ); @content: @p(--mode); place-content: end center; font-size: .5em;
@r, @R, @rn
scale: @r(0, 1); rotate: @r(0, 1turn);
scale: @R(0, 1); rotate: @R(0, 1turn);
scale: @R(0, 1, 3); rotate: @R(0, 1turn, 3);
@grid: 24 / 61.8vh 80vh / #f1fbff; @seed: 1718264392842; scale: @R(0, 2, 2); rotate: @R(±30deg, 2); --c: @p(#155674,#60beb3,#79f8bb,#f5ffae); :after { content: @p('⬮', '.'); z-index: 1; position: absolute; font-size: @R(4vmin); line-height: 0; color: @p(--c); scale: @R(.1, 2); text-shadow: @m2(@M2.r(±2vmin) 0 @p(--c)); } @random(.05) { :after { color: #ff0000 } } @random(.3) { rotate: @R(±90deg, 2); :after { z-index: 0; @size: 1px @R(0, 30px, 3); background: linear-gradient(@p(--c) @R(50%), #0000); } } @random(.1) { :before { content: '.'; position: absolute; color: @p(--color); opacity: @R1; text-shadow: @m24(@plot(r: @R(3em)) 0 currentColor); } }
Random seed
<css-doodle seed="anything"> @seed: anything; /* ... */ </css-doodle>
generator functions
@m @M @rep @mirror @Mirror @cycle @reverse
@m(10, 0)
@m10(0)
@M10(0)
@M10(@n)
@M10.n
@M12-2.n
@mirror.m5.n
@Mirror.m5.n
@cycle.m5.n
@grid: 1 / 75vh; background: @m100( conic-gradient( from @pd(±15, ±30, ±45, ±60, ±90)deg, @m100(red, blue, yellow) ) @pl(0%, 100%) 100% / 2000% 2000% no-repeat ); background-blend-mode: difference; filter: invert(1) hue-rotate(220deg);
background functions
background: @doodle() background: @shaders() background: @pattern() background: @svg() background: @svg-polygon()background: @canvas()
CSS background is like a mirror or a digital screen, everything inside it is virtual and untouchable since there's no actual DOM inside there. But it gives us a window for imagination and a bridge to connect other things.
background: @doodle( @grid: 2x2; background: @p(red, blue); )
@grid: @p(4, 5) / 75vh _1px; --c: #FDF9EB, #BADFDB, #4ABFB8, #FF8B5C, #E63946, #F1FAEE, #A8DADC, #457B9D; background: @p(@p(--c), @m2.doodle( @grid: @r4 _1px; background: @p(@p(--c), @m3.doodle( @grid: @r4 _1px; background: @p(@p(--c), @m2.doodle( @grid: @r4 _1px; background: @p(--c); )) )) ))
@grid: 1 / 60vmin; background: @shaders( void main() { vec2 p = gl_FragCoord.xy / u_resolution.xy; FragColor = vec4(p.xy, .8, 1.); } )
@grid: 1 / 75vh; background: @pattern( grid: 71; fill: #333; match(((int(x*y*x*y*7.)>>4)&2) == 2) { fill: #fff; } )
@grid: 1 / 75vh / #283944; --c: #fff, #fb56ce, #88ffea, #ffd52d, #65b5b5; background: @svg( viewBox: -50 -50 100 100; circle*20 { r: @nN(*30, 4); fill: none; stroke: @p(--c); stroke-linecap: round; stroke-width: @r(.5, 1.5); stroke-dasharray: @nN(*90, 10); transform: rotate(@nN(* -720)); } )
Experimenting A New Syntax To Write SVG
https://yuanchuan.dev/experimenting-a-new-syntax-to-write-svg
Shapes
@shape: heart; /* or */ clip-path: @shape(heart);
clip-path: @shape( fill:nonzero | evenodd; frame:number for frame size; points:number between 3 - 3600; rotate:number in degree for rotation; scale:number for scale factor; move:a pair of value for translating x, y coords; turn:angle between start/end point, defaults to be 1; x:x coordinate for cartesian equation; y:y coordinate for cartesian equation; r:polar equation; )
points: 360; scale: .5; x: cos(2t) + cos(7t); y: sin(2t) + sin(7t); fill: evenodd;
points: 360; scale: .49; x: sin(5t) + sin(4t); -y: cos(4t) + cos(10t);
points: 1000; scale: .061 .06; x: (11*cos(.6t) + cos(55t) - 2.8) * -1.81; -y: (11*sin(9.03t) - sin(77t)) * 2.5 + 17;
points: 480; scale: .3; move: 0 .35; x: sin(t) + sin(6t) + tan.sin(2t); y: cos(t) + cos(5t) + tan.cos(8t);
Playground
Notes about shapes
@grid: 1 / 70vh border; background: #000; clip-path: @shape( points: 1000; scale: .8; move: .5 .64; x: cos(t^t) + cos(1.8^t); y: sin(t) + sin(2.305t)*sin(t); )