Próbáltál már animálni egy linear-gradient() színátmenetét CSS-ből, tisztán transition-nel? Ha igen, valószínűleg belefutottál abba, hogy a színek nem szépen átfolynak egymásba, hanem egyik pillanatról a másikra átugranak. A hiba nem a gradiensben van, hanem abban, hogy a böngésző a custom property-det (--valami) alapból egyszerű szövegként kezeli – nem tudja, hogy az egy szín, egy szög vagy egy szám, ezért nem is tud interpolálni közte. Erre ad megoldást a @property at-rule, ami néhány éve minden nagy böngészőben elérhető, mégis kevesen használják.

A custom property csak egy string – ez a probléma gyökere
Amikor ezt írod:
:root {
--arany: 40%;
}
a böngésző szemszögéből --arany értéke nem egy százalék, hanem egy tetszőleges token-sorozat, ami éppen úgy néz ki, mint egy százalék. A var() a felhasználás helyén egyszerűen behelyettesíti a szöveget, és csak ott derül ki, hogy értelmes-e az adott kontextusban. Ennek két kellemetlen következménye van:
- Egy nem regisztrált custom property-t nem lehet animálni vagy átmenetesíteni – a böngésző nem tudja, hogyan kell két string között „köztes” értéket számolni, ezért a
transitionvagy skip-eli az egészet, vagy egy adott ponton (általában 50%-nál) ugrik az új értékre. - Nincs alapértelmezett érték és nincs öröklődés-szabályozás – ha valahol elfelejted beállítani, a
var()a megadott fallbackre vagy semmire esik vissza.
Amit a @property hozzáad: típus, alapérték, öröklődés
A @property a CSS Properties and Values API (a CSS Houdini egyik moduljának) az a része, amit tisztán CSS-ből, JavaScript nélkül is használhatsz. Három dolgot deklarálsz vele:
@property --arany {
syntax: "<percentage>";
inherits: false;
initial-value: 40%;
}
.sav {
background: linear-gradient(to right, #0d3a52 var(--arany), #28c7b8 var(--arany));
transition: --arany 0.6s ease;
}
.sav:hover {
--arany: 100%;
}
A syntax mondja meg a böngészőnek, milyen típusú érték engedhető meg (<color>, <length>, <angle>, <number>, ezek kombinációi, vagy a mindent elfogadó "*"). Az inherits eldönti, hogy öröklődjön-e gyerek elemekre – ez kötelező mező, nincs hallgatólagos alapértelmezés. Az initial-value pedig azt az értéket adja, amit a property akkor vesz fel, ha semmi mást nem állítottál be, vagy ha a beállított érték érvénytelenné válik.
Ha a syntax típusa ismert (nem "*"), a böngésző pontosan tudja, hogyan kell két érték között interpolálni – egy <percentage>-nél lineárisan, egy <color>-nál a színtérben, egy <angle>-nél fokban. Ettől lesz animálhatóvá a fenti gradiens: nem a linear-gradient() változott, hanem az, hogy a benne lévő custom property most már típusos érték, nem sima szöveg.
A gyakran kihagyott rész: mi történik érvénytelen értéknél
Ez a rész ritkán kerül elő bevezető cikkekben, pedig hibakeresésnél ez okozza a legtöbb fejvakarást. Ha egy regisztrált property-nek olyan értéket adsz, ami nem illeszkedik a syntax-ára – mondjuk a fenti --arany-nak red-et –, az a deklaráció invalid at computed-value time (IACVT) állapotba kerül. Ilyenkor a böngésző nem a korábbi cascade-értékhez nyúl vissza, hanem egyenesen az initial-value-ra esik vissza – csendben, hibaüzenet nélkül. Ez a specifikáció explicit szabálya, és pont azért fontos ismerni, mert az eredmény néha meglepő: egy elgépelt vagy JS-ből rosszul összerakott érték nem a „korábbi jó állapotot” hagyja meg, hanem visszaugrik az alapértékre.
Az initial-value-nak emiatt computationally independent, azaz kontextustól független értéknek kell lennie – 200px vagy 45deg jó, de egy em-ben megadott érték (ami a szülő betűméretétől függ) nem, mert a regisztráció idején még nincs kontextus, amihez viszonyítani lehetne.
Ugyanez JavaScriptből
Ha dinamikusan, futásidőben szeretnél property-t regisztrálni, ott van a CSS.registerProperty() statikus metódus, ami ugyanazokat a mezőket várja, csak JS objektumként:
CSS.registerProperty({
name: "--arany",
syntax: "<percentage>",
inherits: false,
initialValue: "40%",
});
Ha ugyanaz a property mindkét helyen regisztrálva van, a JS-es regisztráció nyer. Duplikált @property szabálynál (két azonos nevű blokk a stíluslapon) egyszerűen az utolsó érvényes deklaráció érvényesül – nincs hiba, csak felülírás.
Hol van ennek gyakorlati haszna
- Konikus és lineáris gradiensek animálása – például egy körkörös progress-jelző, ahol a kitöltési szöget vagy százalékot animálod, nem pedig egy teljesen más elemet mozgatsz a gradiens fölé maszkolással.
- Színátmenetek hover/fókusz állapotok között, amikor a szín maga egy összetettebb
backgroundvagybox-shadowkifejezés része, amit sima property-vel nem lehetne animálni. - Type safety nagyobb design system-ekben: ha egy csapattárs véletlenül
10-et ír10pxhelyett egy regisztrált<length>property-be, az érvénytelen lesz és az alapértékre esik vissza ahelyett, hogy csendben elrontaná a layoutot valahol lentebb a cascade-ben.
Böngészőtámogatás és korlátok
A @property ma már minden nagy böngészőben támogatott: Chrome/Edge 85 óta (2020), Safari 16.4 óta (2023), Firefox pedig 128 óta (2024) – utóbbi volt a legutolsó, ezért lett a funkció csak 2024-ben Baseline. Ha régebbi böngészőt is ki kell szolgálnod, a nem regisztrált fallback (sima var(), JS-es interpoláció) továbbra is használható, a @property pedig progresszív rétegként ül rá – ha a böngésző nem ismeri az at-rule-t, egyszerűen figyelmen kívül hagyja, és a property visszaesik a régi, típus nélküli viselkedésre.
Fontos korlát, hogy a syntax nem tetszőleges: a specifikáció egy zárt listát ad meg az elfogadott típusokra (<length>, <number>, <percentage>, <color>, <image>, <url>, <integer>, <angle>, <time>, <resolution>, <transform-function>, <custom-ident>, ezek listái, illetve a mindent elfogadó "*") – saját, összetett struktúrát nem lehet definiálni. Ha a property-nek nincs értelmezhető típusa, marad a "*", de akkor animáció szempontjából ugyanott vagy, mint egy sima custom property-nél.
A lényeg, amit érdemes megjegyezni: a @property nem egy új CSS-funkció a szó hagyományos értelmében, hanem egy metaszintű eszköz, ami a meglévő custom property-mechanizmust teszi típusossá – és pont ez a típusosság az, ami az animálhatóságot, az alapértéket és a kiszámítható hibaviselkedést adja hozzá.