clip-path: polygon( 0 0, 100% 0, 50% 1% ); background-color: hsla( calc(3.5 * @index() + 180), 80%, 70%, @rand(.8) ); animation: ani 1s cubic-bezier(.175, .885, 0.32, 1.275); animation-delay: @rand(.1s, 1.5s); animation-fill-mode: forwards; @keyframes ani { to { clip-path: polygon( 0 0, 100% 0, @rand(20%, 80%, .1) @rand(20%, 100%, .1) ); } }

< css-doodle />

A web component for drawing patterns with CSS

Introduction

<css-doodle /> is based on Shadow DOM v1 and Custom Elements v1. You can use it on latest Chrome and Safari right now without polyfills.

The component will generate a grid of divs by the rules (plain CSS) inside it. You can easily manipulate those cells using CSS to come up with a graphic pattern or an animated graph. The limit is the limit of CSS itself.

Getting Started

Include the script directly from cdnjs and everything is ready:

If cdnjs is not ready, just use:

You can also install it from npm and import the module in javascript:

Usage

The syntax of <css-doodle /> is based on CSS and provides several extra utility functions and shorthand properties.

:doodle { @grid: 11 / 100vmax; background: #0a0c27; } --n: calc( @abs(@abs(@row() - 6) + @abs(@col() - 6) - 11) / 11 ); margin: -10%; transform: rotate(45deg); border: 1px solid; border-color: hsla( calc(var(--n) * 280 + 120), 60%, 68%, var(--n) );

Grid

The number of rows and columns in the grid is defined by the "grid" attribute on the element, ranged from 1 to 32. It's default to be 1x1 when no value or 0 is given,

:doodle { @size: 7em; } background: #60569e; margin: .5px;

The following formats of "grid" value are recognizable:

  • grid = "0"
  • grid = "5"
  • grid = "20"
  • grid = "5x7"
  • grid = "5 x 7"
  • grid = "5,7"
  • grid = "5,7"

Selectors

:doodle

The :doodle is a special selector indicates to the component element itself. Note that the styles would be over-written by your normal css files outside. (try to hover on the doodle)

:doodle { @grid: 5 / 7em; --s: 0; } :doodle(:hover) { --s: 1; } --offset: calc(var(--s) * 100%); transform: translateY(var(--offset)); will-change: transform; transition: .5s cubic-bezier(.175, .885, .32, 1.275) @rand(500ms); transform-origin: 50% 50%; background: #60569e; margin: .5px;

@even

Select cells like :nth-child(even) but shorter.

:doodle { @size: 7em; } @even { background: #60569e; :after { content: '@index()'; font-size: .7em; color: #fff; } }

@odd

Select cells like :nth-child(odd).

:doodle { @size: 7em; } @odd { background: #60569e; :after { content: '@index()'; font-size: .7em; color: #fff; } }

@nth(n)

Select the nth cell like :nth-child(n).

:doodle { @size: 7em; } background: #f5f5f5; margin: .5px; @nth(9) { background: #60569e; :after { content: '@index()'; font-size: .7em; color: #fff; } }

@at(row, col)

Select cell at specific row and column.

:doodle { @size: 7em; } background: #f5f5f5; margin: .5px; @at(4, 2) { background: #60569e; :after { content: '@row(), @col()'; font-size: .5em; color: #fff; } }

@random

Select cells randomly.

:doodle { @size: 7em; } background: #f5f5f5; transition: .2s; @random { background: #60569e; :after { content: '@index()'; font-size: .7em; color: #fff; } } margin: .5px;

@row(n)

Select the nth row of the grid.

:doodle { @size: 7em; } background: #f5f5f5; margin: .5px; @row(3) { background: #60569e; :after { content: '@row()'; font-size: .7em; color: #fff; } }

The odd and even is supported.

:doodle { @size: 7em; } background: #f5f5f5; margin: .5px; @row(even) { background: #60569e; :after { content: '@row()'; font-size: .7em; color: #fff; } } @row(even) { :after { content: '@row()'; font-size: .7em; color: #fff; } }

@col(n)

Select the nth column of the grid.

:doodle { @size: 7em; } background: #f5f5f5; margin: .5px; @col(3) { background: #60569e; :after { content: '@col()'; font-size: .7em; color: #fff; } }

You can use odd and even too.

:doodle { @size: 7em; } background: #f5f5f5; margin: .5px; @col(odd) { background: #60569e; :after { content: '@col()'; font-size: .7em; color: #fff; } }

Properties

@grid

Another way to set the "grid" attribute and the rule has higher priority.

:doodle { @grid: 3 / 7em; } background: #60569e; margin: .5px;

Set grid and doodle size at the same time:

:doodle { @grid: 8 / 7em; } background: #60569e; margin: .5px;

The row or column is limited up to 1024 only when the grid is 1-dimensional:

:doodle { @grid: 30x1 / 7em 12em; } transition: .2s ease; transition-delay: @rand(500ms); background: #60569e; will-change: width; width: @rand(5%, 100%); margin: .5px 0;

@use

Import styles from custom properties. It lets you write styles from normal css files.

@use: var(--my-rule); margin: .5px;

You can add multiple rules in a natural way:

Or define it in the use attribute on the element.

@size, @min-size, @max-size

Set width and height in one place.

:doodle { @grid: 1 / 7em; } @size: 10em; @size: 4em 5em; @min-size: 7em; @max-size: 7em; background: #60569e; :after { content: '7em x 7em'; color: #fff; font-size: .7em; }

@place-cell

Place cells relative to the grid.

:doodle { @grid: 1x5 / 7em; border: 1px solid #60569e; } @size: 1.6em; background: rgba(72, 74, 142, .8); @nth(1) { @place-cell: 0; } @nth(2) { @place-cell: 100% 25%; } @nth(3) { @place-cell: center; } @nth(4) { @place-cell: .8em calc(100% - .8em); } @nth(5) { @place-cell: 75% 80%; } :after { content: '@index()'; color: #fff; font-size: .7em; }

@shape

Return a css shape generated with clip-path and polygon().

:doodle { @grid: 7 / 7em; @shape: circle; } @even { @shape: hypocycloid 4; background: #60569e; transform: scale(2) rotate(-60deg); }

Basic shapes
:doodle { @grid: 7 / 100vmax; background: #0a0c27; } @shape: clover 5; background: hsla( calc(360 - @index() * 4), 70%, 68%, @rand(.8) ); transform: scale(@rand(.2, 1.5)) translate( @rand(-50%, 50%), @rand(-50%, 50%) );

Functions

@index

Returns the current index value of the cell.

:doodle { @size: 7em; } background: #60569e; margin: .5px; :after { content: '@index()'; color: #fff; font-size: .7em; }

@row

Returns the current row number of the cell.

:doodle { @size: 7em; } background: #60569e; margin: .5px; :after { content: '@row()'; color: #fff; font-size: .7em; }

@max-row

Returns the max row number of the grid.

:doodle { @grid: 4x2 / 7em 9em; } background: #60569e; margin: .5px; :after { content: '@row() / @max-row()'; color: #fff; font-size: .7em; }

@col

Returns the current column number of the cell.

:doodle { @size: 7em; } background: #60569e; margin: .5px; :after { content: '@col()'; color: #fff; font-size: .7em; }

@max-col

Returns the max column number of the grid.

:doodle { @grid: 2x4 / 7em 9em; } background: #60569e; margin: .5px; :after { content: '@col()/@max-col()'; color: #fff; font-size: .7em; }

@size

Returns the total cells number of the grid.

:doodle { @grid: 3 / 7em; } background: #60569e; margin: .5px; :after { content: '@index() / @size()'; color: #fff; font-size: .7em; }

These numbers can be used to generate dynamic values together with calc().

:doodle { @size: 7em; } --alpha: calc(@row() * @col() / @size()); background: rgba(96, 86, 158, var(--alpha)); :after { content: '@calc(@row() * @col())'; color: #fff; font-size: .7em; }

@pick(v1, v2,...)

Randomly pick a value from the given list.
(try to click on the doodle)

:doodle { @size: 7em; } :after { content: '@pick(+, *, -, #)'; color: @pick(#60569e, #2196f3); }

@rand(start [,stop] [,step])

Returns a random value from the range of numbers.
(try to click the doodle)

:doodle { @size: 7em; } background: rgba(96, 86, 158, @rand(.9)); transition: .2s ease @rand(200ms); will-change: transform; transform: rotate(@rand(360deg)); clip-path: polygon( @rand(100%) 0, 100% @rand(100%), 0 @rand(100%) );

It also recognizes the letter range:

:doodle { @size: 7em; } background: #60569e; margin: .5px; :after { font-size: .7em; content: '@rand(a, z)'; color: #fff; }

@repeat(times, value)

Duplicate the given value:
(try to click the doodle)

:doodle { @size: 7em; } background: #60569e; margin: .5px; :after { content: ''; position: absolute; left: @rand(20%, 80%); top: @rand(20%, 80%); @size: 5px; border-radius: 50%; background: #@repeat(6, @pick(@rand(0, 9), @rand(a, f))); }

@<Math>

All Math functions and constants are available prefixed with '@'.

:doodle { @size: 7em; } --num: @abs(@abs(@row() - 3) + @abs(@col() - 3) - 5); background: rgba( 96, 86, 158, calc(var(--num) / 5) ); will-change: transform; transform: rotate(15deg) scale(calc(var(--num) / 5));

:doodle { @grid: 60x1 / 7em 15em; } @size: 75.8% 1px; justify-self: center; background: rgba( 96, 86, 158, calc(1 - @index() / 60) ); will-change: transform; transform: rotate(-15deg) translateX( calc(@sin(@index() / 4) * @PI() * 10px) );

@calc(expr)

Evaluate calculations.

:doodle { @grid: 5 / 7em; } background: #60569e; :after { content: '@calc(@index() * @index())'; color: #fff; font-size: .5em; } @odd { transform: scale(.75); }

@hex(num)

Transform a number to the hex format.

:doodle { @size: 7em; } margin: .5px; :after { content: '\@hex(@rand(9632, 9687))'; color: #60569e; }

:doodle { @grid: 20 / 100vmax; background: #12152f; font-family: sans-serif; } :after { content: '\@hex(@rand(0x2500, 0x257f))'; font-size: 5vmax; color: hsla( @rand(360), 70%, 70%, @rand(.9) ); }

JS API

grid

Getter/setter for the grid attribute on the element.

use

Getter/setter for the use attribute on the element.

update([styles])

Re-render the component (with given styles).

@size: .8em; background: hsla(calc(360 / 60 * @index()), 80%, 80%, .4); clip-path: polygon( 50% 0, 100% 100%, 0 100%); transform: rotate(@rand(360deg)) scale(@rand(.6, 1, .1)) translate( @rand(-10em, 10em), @rand(-10em, 10em) )