CSS Houdinin käyttö: Luo mukautettuja tehosteita joita ei ole tavallisessa CSS:ssä
CSS Houdinin käyttö: Luo mukautettuja tehosteita joita ei ole tavallisessa CSS:ssä
Kokeile tehdä jotain näennäisen yksinkertaista: animoi pehmeästi kulmaa conic-gradient()-funktiossa hoverilla. Määrittelet custom propertyn --kat: 0deg, lisäät transition: --kat 0.4s, viet hiiren päälle – eikä mitään tapahdu. Gradientti vaihtuu hyppäyksittäin, ikään kuin transition-ominaisuutta ei olisi olemassakaan. Tämä ei ole virhe koodissasi. Selaimelle --kat on tavallinen, läpinäkymätön merkkijono – se ei tiedä, että kyseessä on kulma, joten sillä ei ole keinoa interpoloida sitä arvojen 0deg ja 180deg välillä. Se osaa interpoloida lukuja, värejä, pituuksia – koska se tuntee niiden tyypit. Custom propertyillä, toisin kuin CSS:n sisäänrakennetuilla ominaisuuksilla, ei ole minkäänlaista tyyppiä. Juuri tähän aukkoon CSS Houdini asettuu.
Mitä Houdini todella on
Tämä on yleinen väärinkäsitys: Houdini ei ole yksi teknologia eikä edes yksi spesifikaatio. Se on sateenvarjo useille toisistaan riippumattomille W3C:n ehdotuksille, joita yhdistää yksi ajatus – paljastaa palasia renderöintimoottorista (arvojen jäsennys, layout, maalaus, animaatio), jotka olivat aiemmin täysin piilossa kehittäjältä. Sen sijaan, että odottaisit CSS:ään valmista ominaisuutta, joka tekee juuri sen mitä tarvitset, saat matalan tason koukun tiettyyn prosessin vaiheeseen.
Nämä koukut toteutetaan workletteina – pieninä JavaScript-moduuleina, jotka ovat käsitteellisesti lähellä Web Workereita, mutta suunniteltu erityisesti renderöintiputkea varten. Worklet toimii pääsäikeen ulkopuolella, sillä ei ole pääsyä kohteisiin window, document tai DOM, ja se kommunikoi ainoastaan tarkasti määritellyn sopimuksen kautta (esim. paint(ctx, size, properties)). Tämä ei ole spesifikaation laatijoiden laiskuudesta johtuva rajoitus – se on tietoinen arkkitehtuuripäätös. Eristyksen ansiosta moottori voi ajaa workletteja rinnakkain, erillisissä säikeissä, ja välimuistittaa tuloksen ilman riskiä, että koodisi koskettaisi jotain, mitä sen ei pitäisi. Sama eristys on syynä siihen, miksi workletin sisältä ei löydy Date-, performance.now()- tai fetch-toimintoja – tarkan ajan ja verkkoyhteyden käytön rajoittaminen on tietoista suojaa timing- ja fingerprinting-tyyppisiä hyökkäyksiä vastaan sivun renderöintikoodin tasolla.
Tämän sateenvarjon eri osat ovat tänään aivan eri vaiheissa: yksi on ollut pitkään osa Baselinea, toinen toimii vain Chromessa, ja vielä yksi on vuosikymmenen jälkeenkin edelleen kokeellinen. Tämä erottelu on olennainen – ja useimmat Houdiniin johdattelevat artikkelit sivuuttavat sen, käsitellen kokonaisuutta yhtenä, käyttövalmiina teknologiana.
Ainoa Houdinin osa, jota kannattaa oikeasti käyttää jo nyt: @property
CSS Properties and Values API ratkaisee juuri sen ongelman, josta tämä artikkeli avautui. Rekisteröit custom propertyn tietyllä tyypillä (syntax), alkuarvolla ja tiedolla siitä, periytyykö se – ja siitä hetkestä lähtien selain kohtelee sitä täysivaltaisena, animoitavana arvona eikä merkkijonona.
html
<div class="ring"></div>
css
@property --kat {syntax: '<angle>';inherits: false;initial-value: 0deg;}.ring {width: 200px;height: 200px;border-radius: 50%;background: conic-gradient(from var(--kat), #ff4d4d, #4d79ff, #4dff88, #ff4d4d);transition: --kat 0.6s ease;}.ring:hover {--kat: 180deg;}
Tämä esimerkki tekee todella jotain, mihin tavallinen CSS ei pysty – ilman @property-sääntöä yllä oleva transition ei yksinkertaisesti toimisi, aivan kuten artikkelin alun skenaariossa. syntax: '<angle>' kertoo selaimelle, että --kat on kulma, joten sitä voidaan interpoloida sulavasti; inherits: false estää tahattoman periytymisen lapsielementteihin, mikä custom propertyjen kohdalla on usein vaikeasti jäljitettävien virheiden lähde.
Saman lopputuloksen voi saavuttaa myös JavaScriptin puolelta, mikä on järkevää silloin, kun rekisteröit ominaisuuksia dynaamisesti (esim. datan perusteella generoituna) sen sijaan, että kirjoittaisit ne kiinteästi tyylitiedostoon:
javascript
if ('registerProperty' in CSS) {CSS.registerProperty({name: '--kat',syntax: '<angle>',inherits: false,initialValue: '0deg',});}
Tämä on ainoa Houdinin osa, jolla on tänään Baseline-status – se toimii natiivisti Chromessa, Firefoxissa (versiosta 128 alkaen) ja Safarissa (versiosta 16.4 alkaen), ilman polyfillejä ja ilman @supports-sääntöä. Jos koko Houdinista pitäisi muistaa ja ottaa käyttöön vain yksi asia, se olisi juuri tämä.
CSS Paint API: worklet, joka oikeasti maalaa
Paint API mahdollistaa background-ominaisuuden korvaamisen paint()-funktiolla, joka piirtää suoraan elementille käyttäen riisuttua, Canvas 2D:tä muistuttavaa rajapintaa (PaintRenderingContext2D) – ilman fillText-, drawImage-toimintoja tai pikselien lukemista. Se, mikä erottaa tämän lähestymistavan tavallisesta <canvas>-elementille piirtämisestä, on inputProperties: worklet ilmoittaa, mitä custom propertyjä se haluaa tarkkailla, ja piirtyy uudelleen automaattisesti vain silloin, kun jokin niistä muuttuu – ilman ainuttakaan JavaScript-riviä tapahtumien kuunteluun.
Alla on esimerkki varoittavista, vinoista raidoista, joiden kulmaa ohjataan samalla, aiemmin rekisteröidyllä --kat-ominaisuudella (tarkemmin sanoen sen paikallisella muunnelmalla --stripe-angle) – tämä havainnollistaa, kuinka @property ja Paint API täydentävät toisiaan luontevasti.
html
<div class="hazard"></div>
css
@property --stripe-angle {syntax: '<angle>';inherits: false;initial-value: 45deg;}.hazard {width: 100%;height: 120px;--stripe-angle: 45deg;background: paint(hazardStripes);transition: --stripe-angle 0.4s ease;}.hazard:hover {--stripe-angle: 135deg;}
javascript
// main.jsif ('paintWorklet' in CSS) {CSS.paintWorklet.addModule('hazard-stripes.js');}
javascript
// hazard-stripes.jsclass HazardStripesPainter {static get inputProperties() {return ['--stripe-angle'];}paint(ctx, size, properties) {const angle = properties.get('--stripe-angle').to('deg').value;const stripeWidth = 24;const { width, height } = size;const diagonal = Math.sqrt(width ** 2 + height ** 2) * 2;ctx.save();ctx.translate(width / 2, height / 2);ctx.rotate((angle * Math.PI) / 180);ctx.translate(-diagonal, -diagonal);let stripeIndex = 0;for (let x = 0; x < diagonal * 2; x += stripeWidth) {ctx.fillStyle = stripeIndex % 2 === 0 ? '#111111' : '#f5c400';ctx.fillRect(x, 0, stripeWidth, diagonal * 2);stripeIndex++;}ctx.restore();}}registerPaint('hazardStripes', HazardStripesPainter);
Kannattaa kiinnittää huomiota kohtaan properties.get('--stripe-angle').to('deg').value – kyseessä on CSS Typed OM, saman API-perheen toinen jäsen. Sen sijaan, että jäsentäisit merkkijonon käsin (parseFloat, "deg"-osan leikkaaminen pois), saat tyypitetyn CSSUnitValue-arvon, jonka muunnat eksplisiittisesti tarvitsemaasi yksikköön. Koska piirrettyjen raitojen kulma riippuu yksinomaan inputProperties-listassa ilmoitetusta custom propertystä, moottori tietää täsmälleen, milloin elementti täytyy piirtää uudelleen – jonkin sivun muun, tähän liittymättömän CSS-ominaisuuden muuttuminen ei käynnistä paint()-funktiota uudelleen.
Layout API ja Animation Worklet: vuoden 2026 todellisuus
Tästä alkaa osuus, josta useimmat Houdiniin johdattelevat artikkelit vaikenevat. CSS Layout API – saman spesifikaation kolmas pilari, jonka avulla voi määritellä oman layout-algoritmin (vastine omalle display: grid-toteutukselle) – on ollut suunnitteilla vuodesta 2015 lähtien ja on vuosikymmenen jälkeenkin edelleen kokeellinen. Yksikään vakaa selain ei ole ottanut sitä natiivisti käyttöön; se on saatavilla ainoastaan lippujen takana tai Chromen origin trial -kokeiluissa. Jos törmäät artikkeliin, joka esittää display: layout(nimi)-syntaksin käyttövalmiina ratkaisuna, tarkista julkaisupäivä – kyseessä on todennäköisesti vuosia vanha origin trial -demo, ei mitään, minkä saisit toimimaan tänään käyttäjän selaimessa.
Animation Workletin piti antaa täysi hallinta scrollin ohjaamiin animaatioihin, jotka suoritetaan pääsäikeen ulkopuolella. Käytännössä sitä tukee vain Chromium. Mikä tärkeämpää – sen pääasiallinen käyttötapaus ei ole enää syy turvautua Houdiniin, koska CSS sai natiivin ratkaisun samaan ongelmaan: animation-timeline: scroll() mahdollistaa nykyään animaation sitomisen scrollaukseen ilman yhtäkään JavaScript-riviä ja ilman workletteja, ja selaintuki sille kasvaa jatkuvasti. Tämä havainnollistaa hyvin laajempaa kaavaa: osa syistä, joiden vuoksi Houdini ylipäätään syntyi, päätyi ajan myötä suoraan CSS-spesifikaatioon sen sijaan, että olisi jäänyt kehittäjille tarkoitetuksi matalan tason API:ksi.
Milloin tästä on käytännössä hyötyä
- Käytä
@property-sääntöä rohkeasti, ilman varmistuksia – sillä on Baseline-status, eikä se tarvitse@supports-sääntöä tai polyfillejä. - Kohtele Paint API:a progressiivisena parannuksena – kääri se
@supports(background: paint(x))-sääntöön ja tarjoa järkevä varafallback puhtaalla CSS:llä (esim. tavallisella gradientilla) Firefoxia varten, tai käytäcss-paint-polyfill-polyfilliä, jos tehosteen täytyy toimia kaikkialla. - Älä suunnittele tuotanto-ominaisuutta Layout API:n varaan – se on yhä kokeellinen, ilman todellista selaintukea, hyvä leikkimiseen mutta ei roadmapille.
- Muista, että
paint()ei ole „ilmaista” CSS:ää – se on omaa JavaScript-koodiasi, joka suoritetaan uudelleen jokaisellainputProperties-listassa ilmoitetun ominaisuuden muutoksella. Raskaat laskennat (kohinan generointi, monimutkaiset proseduraaliset kuviot) täytyy optimoida aivan kuten<canvas>-elementin renderöintisilmukka – tässä on helppo rakentaa vahingossa animaatio, joka piirtää koko elementin uudelleen kuva kuvalta layoutin pääsäikeessä.
Yhteenveto
Houdini ei ole yksi „käytetäänkö vai ei” -päätös – se on kolme, neljä erillistä päätöstä, joilla kullakin on erilainen riski. Ensinnäkin @property ratkaisee todellisen, konkreettisen ongelman (custom propertyjen animoinnin) ja on tänään yhtä turvallinen käyttää kuin mikä tahansa muu CSS-ominaisuus – se on ainoa tämän ekosysteemin osa, joka kannattaa ottaa käyttöön epäröimättä. Toiseksi Paint API antaa todellista voimaa (worklet, joka piirtyy uudelleen vain ilmoitettujen riippuvuuksien muuttuessa, ilman manuaalista tapahtumien kuuntelua), mutta se vaatii tietoisen suunnitelman selaimille, joilla tukea ei ole – se on työkalu progressiiviseen parantamiseen, ei kriittisiin ominaisuuksiin. Kolmanneksi Layout API ja Animation Worklet ovat hyvä esimerkki siitä, ettei spesifikaatio itsessään takaa käyttöönottoa – vuosien jälkeen toinen odottaa yhä toteutusta, ja toinen on osittain korvattu yksinkertaisemmalla, natiivilla CSS:llä. Ennen kuin tartut Houdiniin, tarkista, mihin näistä kolmesta korista täsmälleen se osa, jota tarvitset, kuuluu.