start . me .
Directory path . web .

HTML Document File : program-css.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>programmable CSS</title>
</head>
<body style="background-color: lightgrey;">

<pre>programmable CSS, variables in CSS (and so on)!
button { width: $.side; height: $.side; }
onclick=" $.side++ "</pre>

<button onclick="$.side=100">set side 100</button>
<button onclick="$.side=50">set side 50</button>
<br>

<button onclick="$.colors.push('green'); buttonClassStyle.color='lightgrey' ">add element: green</button>
<button onclick="$.colors.push('red'); buttonClassStyle.color='lightgrey' ">add element: red</button>
<button onclick="$.colors=[]">no elements</button>
<br><br>
<style>button { height: 4em; } </style>

<canvas id="canvas1" width="300px" height="200px" style="width: calc(100% - 2px); border: 1px solid black;"></canvas>
<script>
var $ = optionallyCallable( {} )
$.colors = ['red']
$.count = _=> _.colors.length
$.side = 50

// button { width: $.side; height: $.side; }
var buttonClassStyle = { width: ()=> $.side , height: ()=> $.side , paddingRight: 10 , color: "yellow" , paddingBottom: 10 }
buttonClassStyle = optionallyCallable(buttonClassStyle) // /web/optionally-callable.html

var buttonClassEventHandler = function(event) { alert("event triggered!"); buttonClassStyle.color="white" }

function optionallyCallable(objectToProxy) {
    var proxy
    var handlerOptionallyCallable = {
        // handle access
        get(target, accessedPropertyName, receiver) {
            return typeof target[accessedPropertyName] === 'function' ?
                target[accessedPropertyName] ( proxy /* same */ )
                : target[accessedPropertyName]
        },
    }
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
    proxy = new Proxy(objectToProxy, handlerOptionallyCallable)
    return proxy
}

var canvas = document.getElementById("canvas1")
var context = canvas.getContext("2d", { willReadFrequently: true })
function draw(){

    canvas.width = canvas.clientWidth // accounts also for screen-orientation caused screen-resizes

    var maxY = 0 // minimum height

    sensibleAreas = []

    context.clearRect(0, 0, canvas.width, canvas.height)

    function gradientApplier(x0, y0, x1, y1, color) {
        var gradient1 = context.createLinearGradient(x0, y0, x1, y1)
        gradient1.addColorStop(0, buttonClassStyle.color )
        gradient1.addColorStop(1, /* "white" */ color )
        context.fillStyle = gradient1
    }

    var left=0, top=0
    for (var i = 0; i < $.count; i++){
        var width = buttonClassStyle.width; var height = buttonClassStyle.height;

        if( (left+width) > canvas.width ){
            top += height + buttonClassStyle.paddingBottom
            left = 0
        }

        var color = $.colors[i]
        gradientApplier(left, 0, left+width, 0, color)
        context.fillRect(left, top, width, height)

        {
            context.beginPath()
            context.rect(left, top, width, height)
            context.stroke()
        }

        var sensibleRectangle = { x1: left, x2: left+width, y1: top, y2: top+height }
        var onClickEventHandler = buttonClassEventHandler
        sensibleAreas.push({sensibleRectangle,onClickEventHandler})

        left += width + buttonClassStyle.paddingRight

        maxY = Math.max(maxY,sensibleRectangle.y2)
    }

    {
        var previousImage = context.getImageData(0,0, Math.max(canvas.width,1), Math.max(canvas.height,1) )

        canvas.height = maxY

        context.putImageData(previousImage,0,0)
    }
}

setInterval(draw)

sensibleAreas = []
canvas.onclick=function(event){
    function pointInRectangle(p, r) {
      return p.x > r.x1 && p.x < r.x2 && p.y > r.y1 && p.y < r.y2 }
    var point = {x: event.offsetX, y: event.offsetY}

    for (var rectangle of sensibleAreas) {
        if(pointInRectangle(point, rectangle.sensibleRectangle)){
            var event = {}
            rectangle.onClickEventHandler(event)
        }
    }
}
</script>

<script src="/web/show-source.js"></script>

</body>
</html>