Kako ustvariti valovite oblike in vzorce v CSS PlatoBlockchain Data Intelligence. Navpično iskanje. Ai.

Kako ustvariti valovite oblike in vzorce v CSS

Val je verjetno ena najtežjih oblik za izdelavo v CSS. Vedno ga poskušamo približati z lastnostmi, kot je border-radius in veliko čarobnih številk, dokler ne dobimo nečesa, kar se zdi nekako blizu. In to še preden sploh zaidemo v valovite vzorce, ki so težji.

»SVG to!« bi lahko rekli in verjetno imate prav, da je to boljša pot. Videli pa bomo, da lahko CSS naredi lepe valove in ni nujno, da je koda zanj popolnoma nora. In ugani kaj? imam spletni generator da bo še bolj trivialno!

Če se igrate z generatorjem, lahko vidite, da je CSS, ki ga izpljune, le dva preliva in lastnost maske CSS – samo ti dve stvari in lahko naredimo kakršno koli obliko valov ali vzorec. Da ne omenjam, da lahko preprosto nadzorujemo velikost in ukrivljenost valov, ko smo že pri tem.

Nekatere vrednosti so lahko videti kot "magične številke«, a za njimi je pravzaprav logika in razčlenili bomo kodo ter odkrili vse skrivnosti ustvarjanja valov.

Ta članek je nadaljevanje prejšnji kjer sem zgradil vse vrste različnih cik-cak, zaokroženih, nazobčanih in da, valovitih obrob. Zelo priporočam, da preverite ta članek, saj uporablja isto tehniko, ki jo bomo obravnavali tukaj, vendar bolj podrobno.

Matematika za valovi

Strogo gledano, za valovitimi oblikami ni ene same čarobne formule. Vsako obliko s krivuljami, ki gredo navzgor in navzdol, lahko imenujemo val, zato se ne bomo omejili na zapleteno matematiko. Namesto tega bomo reproducirali val z uporabo osnov geometrije.

Začnimo s preprostim primerom z uporabo dveh oblik kroga:

Kako ustvariti valovite oblike in vzorce v CSS

Drug poleg drugega imamo dva kroga z enakim polmerom. Ali vidite tisto rdečo črto? Pokriva zgornjo polovico prvega kroga in spodnjo polovico drugega. Zdaj si predstavljajte, da vzamete to vrstico in jo ponovite.

Vijugasta rdeča črta v obliki valov.
Kako ustvariti valovite oblike in vzorce v CSS

Val že vidimo. Zdaj zapolnimo spodnji del (ali zgornji), da dobimo naslednje:

Vzorec rdečih valov.
Kako ustvariti valovite oblike in vzorce v CSS

takrat! Imamo valovito obliko, ki jo lahko nadzorujemo z eno spremenljivko za polmere kroga. To je eden najlažjih valov, ki jih lahko naredimo, in to je tisti, v katerem sem se pokazal this prejšnji članek

Dodajmo malo zapletenosti, tako da vzamemo prvo ilustracijo in nekoliko premaknemo kroge:

Dva siva kroga z dvema razpolovljenima črtkanima črtama, ki označujeta razmik.
Kako ustvariti valovite oblike in vzorce v CSS

Še vedno imamo dva kroga z enakimi radiji, vendar nista več vodoravno poravnana. V tem primeru rdeča črta ne pokriva več polovice površine vsakega kroga, temveč manjšo površino. To območje je omejeno s črtkano rdečo črto. Ta črta prečka točko, kjer se stikata oba kroga.

Zdaj vzemite to vrstico in jo ponovite in dobili boste nov val, bolj gladek.

Rdeča vijugasta črta.
Kako ustvariti valovite oblike in vzorce v CSS
Vzorec rdečih valov.
Kako ustvariti valovite oblike in vzorce v CSS

Mislim, da ste razumeli. Z nadzorom položaja in velikosti krogov lahko ustvarimo kakršen koli val želimo. Zanje lahko celo ustvarimo spremenljivke, ki jih bom poklical P in SOz.

Kako ustvariti valovite oblike in vzorce v CSS PlatoBlockchain Data Intelligence. Navpično iskanje. Ai.
Kako ustvariti valovite oblike in vzorce v CSS

Verjetno ste opazili, da v spletnem generatorju krmilimo val z dvema vhodoma. Preslikajo se v zgornje spremenljivke. S je "Velikost vala" in P je "ukrivljenost vala".

definiram P as P = m*S Kje m je spremenljivka, ki jo prilagodite, ko posodabljate ukrivljenost vala. To nam omogoča, da imamo vedno enako ukrivljenost, tudi če posodobimo S.

m je lahko katera koli vrednost med 0 in 2. 0 nam bo dal prvi poseben primer, ko sta oba kroga poravnana vodoravno. 2 je nekakšna največja vrednost. Lahko gremo večje, a po nekaj testih sem ugotovil, da vse zgoraj 2 proizvaja slabe, ravne oblike.

Ne pozabimo na polmer našega kroga! To je mogoče definirati tudi z uporabo S in P Všečkaj to:

R = sqrt(P² + S²)/2

Kdaj P je enako 0, bomo imeli R = S/2.

Imamo vse, da začnemo vse to pretvarjati v prelive v CSS!

Ustvarjanje gradientov

Naši valovi uporabljajo kroge in ko govorimo o krogih, govorimo o radialnih gradientih. In ker dva kroga definirata naš val, bomo logično uporabili dva radialna gradienta.

Začeli bomo s konkretnim primerom P je enako 0. Tukaj je ilustracija prvega gradienta:

Ta gradient ustvari prvo ukrivljenost, medtem ko zapolni celotno spodnje območje - tako rekoč "vodo" vala.

Kako ustvariti valovite oblike in vzorce v CSS PlatoBlockchain Data Intelligence. Navpično iskanje. Ai.
Kako ustvariti valovite oblike in vzorce v CSS
.wave {
  --size: 50px;

  mask: radial-gradient(var(--size) at 50% 0%, #0000 99%, red 101%) 
    50% var(--size)/calc(4 * var(--size)) 100% repeat-x;
}

O --size spremenljivka določa polmer in velikost radialnega gradienta. Če ga primerjamo z S spremenljivka, potem je enako S/2.

Zdaj pa dodamo drugi gradient:

Drugi gradient ni nič drugega kot krog za dokončanje našega vala:

radial-gradient(var(--size) at 50% var(--size), blue 99%, #0000 101%) 
  calc(50% - 2*var(--size)) 0/calc(4 * var(--size)) 100%

Če preverite prejšnji članek videli boste, da preprosto ponavljam, kar sem že naredil tam.

Spremljal sem oba članka, vendar konfiguracije gradientov niso enake.

To je zato, ker lahko dosežemo enak rezultat z uporabo različnih konfiguracij gradientov. Če primerjate obe konfiguraciji, boste opazili rahlo razliko v poravnavi, vendar je trik enak. To je lahko zmedeno, če niste seznanjeni s prelivi, vendar ne skrbite. Z nekaj vaje se jih boste navadili in sami boste ugotovili, da lahko različna sintaksa vodi do enakega rezultata.

Tukaj je celotna koda za naš prvi val:

.wave {
  --size: 50px;

  mask:
    radial-gradient(var(--size) at 50% var(--size),#000 99%, #0000 101%) 
      calc(50% - 2*var(--size)) 0/calc(4 * var(--size)) 100%,
    radial-gradient(var(--size) at 50% 0px, #0000 99%, #000 101%) 
      50% var(--size)/calc(4 * var(--size)) 100% repeat-x;
}

Zdaj pa vzemimo to kodo in jo prilagodimo tako, da uvedemo spremenljivko, ki jo naredi popolnoma ponovno uporabno za ustvarjanje katerega koli vala, ki ga želimo. Kot smo videli v prejšnjem razdelku, je glavni trik premikanje krogov, tako da niso več poravnani, zato posodobimo položaj vsakega od njih. Prvega bomo premaknili navzgor, drugega pa navzdol.

Naša koda bo videti takole:

.wave {
  --size: 50px;
  --p: 25px;

  mask:
    radial-gradient(var(--size) at 50% calc(var(--size) + var(--p)), #000 99%, #0000 101%) 
      calc(50% - 2*var(--size)) 0/calc(4 * var(--size)) 100%,
    radial-gradient(var(--size) at 50% calc(-1*var(--p)), #0000 99%, #000 101%) 
      50% var(--size) / calc(4 * var(--size)) 100% repeat-x;
}

Predstavil sem novo --p spremenljivka, ki jo je uporabila za določitev središčnega položaja vsakega kroga. Prvi gradient uporablja 50% calc(-1*var(--p)), zato se njegovo središče premakne navzgor, medtem ko drugi uporablja calc(var(--size) + var(--p)) da ga premaknete navzdol.

Demo je vreden tisoč besed:

Krogi niso niti poravnani niti se ne dotikajo drug drugega. Razmaknili smo jih daleč narazen, ne da bi spremenili njihove radije, zato smo izgubili val. Toda stvari lahko popravimo z uporabo iste matematike, ki smo jo uporabili prej za izračun novega polmera. Zapomni si to R = sqrt(P² + S²)/2. V našem primeru --size je enako S/2; enako za --p kar je tudi enako P/2 saj premikamo oba kroga. Torej je razdalja med njunima središčema dvojna vrednost --p za to:

R = sqrt(var(--size) * var(--size) + var(--p) * var(--p))

To nam daje rezultat 55.9px.

Naš val se vrača! Vključimo to enačbo v naš CSS:

.wave {
  --size: 50px;
  --p: 25px;
  --R: sqrt(var(--p) * var(--p) + var(--size)*var(--size));

  mask:
    radial-gradient(var(--R) at 50% calc(var(--size) + var(--p)), #000 99%, #0000 101%) 
      calc(50% - 2*var(--size)) 0 / calc(4 * var(--size)) 100%,
    radial-gradient(var(--R) at 50% calc(-1*var(--p)), #0000 99%, #000 101%) 
      50% var(--size)/calc(4 * var(--size)) 100% repeat-x;
}

To je veljavna koda CSS. sqrt() je del specifikacije, vendar v času, ko to pišem, brskalnik zanj ne podpira. To pomeni, da potrebujemo nekaj JavaScripta ali Sass za izračun te vrednosti, dokler ne postanemo širši sqrt() Podpreti.

To je prekleto kul: dovolj sta dva preliva, da dobite hladen val, ki ga lahko uporabite za kateri koli element z uporabo mask premoženje. Nič več poskusov in napak – vse, kar morate, je posodobiti dve spremenljivki in pripravljeni ste!

Obrnitev vala

Kaj pa, če želimo, da gredo valovi v drugo smer, kjer napolnimo »nebo« namesto »vode«. Verjeli ali ne, vse kar moramo storiti je, da posodobimo dve vrednosti:

.wave {
  --size: 50px;
  --p: 25px;
  --R: sqrt(var(--p) * var(--p) + var(--size) * var(--size));

  mask:
    radial-gradient(var(--R) at 50% calc(100% - (var(--size) + var(--p))), #000 99%, #0000 101%)
      calc(50% - 2 * var(--size)) 0/calc(4 * var(--size)) 100%,
    radial-gradient(var(--R) at 50% calc(100% + var(--p)), #0000 99%, #000 101%) 
      50% calc(100% - var(--size)) / calc(4 * var(--size)) 100% repeat-x;
}

Vse, kar sem naredil, je, da sem dodal odmik, ki je enak 100%, poudarjeno zgoraj. Tukaj je rezultat:

Razmislimo o bolj prijazni sintaksi z uporabo vrednosti ključnih besed, da bo še lažje:

.wave {
  --size: 50px;
  --p: 25px;
  --R: sqrt(var(--p)*var(--p) + var(--size) * var(--size));

  mask:
    radial-gradient(var(--R) at left 50% bottom calc(var(--size) + var(--p)), #000 99%, #0000 101%) 
      calc(50% - 2 * var(--size)) 0/calc(4 * var(--size)) 100%,
    radial-gradient(var(--R) at left 50% bottom calc(-1 * var(--p)), #0000 99%, #000 101%) 
      left 50% bottom var(--size) / calc(4 * var(--size)) 100% repeat-x;
}

Uporabljamo left in bottom ključne besede za določitev strani in odmika. Brskalnik privzeto nastavi na left in top — zato uporabljamo 100% da premaknete element na dno. V resnici ga premikamo iz top by 100%, torej je res enako kot reči bottom. Veliko lažje brati kot matematiko!

S to posodobljeno sintakso moramo le zamenjati bottom za top — ali obratno — za spremembo smeri valovanja.

In če želite dobiti tako zgornje kot spodnje valove, združimo vse prelive v eno samo deklaracijo:

.wave {
  --size: 50px;
  --p: 25px;
  --R: sqrt(var(--p)*var(--p) + var(--size)*var(--size));

  mask:
    /* Gradient 1 */
    radial-gradient(var(--R) at left 50% bottom calc(var(--size) + var(--p)), #000 99%, #0000 101%) 
      left calc(50% - 2*var(--size)) bottom 0 / calc(4 * var(--size)) 51% repeat-x,
    /* Gradient 2 */
    radial-gradient(var(--R) at left 50% bottom calc(-1 * var(--p)), #0000 99%, #000 101%) 
      left 50% bottom var(--size) / calc(4 * var(--size)) calc(51% - var(--size)) repeat-x,
    /* Gradient 3 */
    radial-gradient(var(--R) at left 50% top calc(var(--size) + var(--p)), #000 99%, #0000 101%) 
      left calc(50% - 2 * var(--size)) top 0 / calc(4 * var(--size)) 51% repeat-x,
    /* Gradient 4 */
    radial-gradient(var(--R) at left 50% top calc(-1 * var(--p)), #0000 99%, #000 101%) 
      left 50% top var(--size) / calc(4 * var(--size)) calc(51% - var(--size)) repeat-x;
}

Če preverite kodo, boste videli, da sem poleg kombinacije vseh prelivov zmanjšal tudi njihovo višino od 100% do 51% tako da oba pokrivata polovico elementa. ja 51%. Potrebujemo ta majhen dodaten odstotek za majhno prekrivanje, ki se izogne ​​vrzeli.

Kaj pa leva in desna stran?

To je tvoja domača naloga! Vzemite tisto, kar smo naredili z zgornjo in spodnjo stranjo, in poskusite posodobiti vrednosti, da dobite desno in levo vrednost. Ne skrbite, preprosto je in edina stvar, ki jo morate storiti, je, da zamenjate vrednosti.

Če imate težave, lahko vedno uporabite spletni generator za preverjanje kode in vizualizacijo rezultata.

Valovite črte

Prej smo naredili naš prvi val z rdečo črto in nato zapolnili spodnji del elementa. Kaj pa tista valovita črta? Tudi to je val! Še bolje je, če lahko nadzorujemo njegovo debelino s spremenljivko, da jo lahko ponovno uporabimo. Naredimo to!

Ne bomo začeli iz nič, temveč bomo vzeli prejšnjo kodo in jo posodobili. Prva stvar, ki jo morate narediti, je, da posodobite barvne meje prelivov. Oba preliva se začneta od prozorne barve do neprozorne ali obratno. Za simulacijo črte ali obrobe moramo začeti s prosojnosti, iti na neprozorno in nato spet nazaj na prosojno:

#0000 calc(99% - var(--b)), #000 calc(101% - var(--b)) 99%, #0000 101%

Mislim, da ste že uganili, da --b spremenljivka je tisto, kar uporabljamo za nadzor debeline črte. Uporabimo to za naše prelive:

Ja, rezultat je daleč od valovite črte. Toda če natančno pogledamo, lahko vidimo, da en gradient pravilno ustvarja spodnjo ukrivljenost. Torej, vse kar moramo storiti je, da popravimo drugi gradient. Namesto da ohranimo polni krog, naredimo delnega kot drugi gradient.

Še daleč, vendar imamo obe krivini, ki jih potrebujemo! Če preverite kodo, boste videli, da imamo dva enaka gradienta. Edina razlika je njihov položaj:

.wave {
  --size: 50px;
  --b: 10px;
  --p: 25px;
  --R: sqrt(var(--p)*var(--p) + var(--size)*var(--size));

  --_g: #0000 calc(99% - var(--b)), #000 calc(101% - var(--b)) 99%, #0000 101%;
  mask:
    radial-gradient(var(--R) at left 50% bottom calc(-1*var(--p)), var(--_g)) 
      calc(50% - 2*var(--size)) 0/calc(4*var(--size)) 100%,
    radial-gradient(var(--R) at left 50% top    calc(-1*var(--p)), var(--_g)) 
      50% var(--size)/calc(4*var(--size)) 100%;
}

Zdaj moramo prilagoditi velikost in položaj za končno obliko. Ne potrebujemo več, da je gradient polne višine, zato ga lahko zamenjamo 100% s tem:

/* Size plus thickness */
calc(var(--size) + var(--b))

Za to vrednostjo ni nobene matematične logike. Le dovolj velika mora biti za ukrivljenost. Njegov učinek na vzorec bomo videli čez nekaj časa. Medtem posodobimo tudi položaj tako, da bodo prelivi navpično središče:

.wave {
  --size: 50px;
  --b: 10px;
  --p: 25px;
  --R: sqrt(var(--p)*var(--p) + var(--size)*var(--size));

  --_g: #0000 calc(99% - var(--b)), #000 calc(101% - var(--b)) 99%, #0000 101%;  
  mask:
    radial-gradient(var(--R) at left 50% bottom calc(-1*var(--p)), var(--_g)) 
      calc(50% - 2*var(--size)) 50%/calc(4 * var(--size)) calc(var(--size) + var(--b)) no-repeat,
    radial-gradient(var(--R) at left 50% top calc(-1 * var(--p)), var(--_g)) 50%
      50%/calc(4 * var(--size)) calc(var(--size) + var(--b)) no-repeat;
}

Še vedno ni čisto tam:

En gradient se mora premakniti nekoliko navzdol, drugi pa nekoliko navzgor. Oba se morata premakniti za polovico svoje višine.

Skoraj smo tam! Potrebujemo majhen popravek za popolno prekrivanje polmera. Obe črti morata biti zamaknjeni za polovico roba (--b) debelina:

Imamo ga! Popolna valovita linija, ki jo lahko enostavno prilagodimo z nadzorom nekaj spremenljivk:

.wave {
  --size: 50px;
  --b: 10px;
  --p: 25px;
  --R: calc(sqrt(var(--p) * var(--p) + var(--size) * var(--size)) + var(--b) / 2);

  --_g: #0000 calc(99% - var(--b)), #000 calc(101% - var(--b)) 99%, #0000 101%;
  mask:
    radial-gradient(var(--R) at left 50% bottom calc(-1 * var(--p)), var(--_g)) 
     calc(50% - 2*var(--size)) calc(50% - var(--size)/2 - var(--b)/2) / calc(4 * var(--size)) calc(var(--size) + var(--b)) repeat-x,
    radial-gradient(var(--R) at left 50% top calc(-1*var(--p)),var(--_g)) 
     50%  calc(50% + var(--size)/2 + var(--b)/2) / calc(4 * var(--size)) calc(var(--size) + var(--b)) repeat-x;
}

Vem, da je treba logiko malo razumeti. To je v redu in kot sem rekel, ustvarjanje valovite oblike v CSS ni enostavno, da ne omenjam zapletene matematike v ozadju. Zato je spletni generator je rešilna bilka — zlahka dobite končno kodo, tudi če ne razumete popolnoma logike za njo.

Valoviti vzorci

Iz valovite črte, ki smo jo pravkar ustvarili, lahko naredimo vzorec!

O ne, kodo vzorca bo še težje razumeti!

Sploh ne! Kodo že imamo. Vse kar moramo storiti je, da odstranimo repeat-x od tega, kar že imamo, in takrat. 🎉

Lep valovit vzorec. Se spomnite enačbe, za katero sem rekel, da jo bomo ponovno pregledali?

/* Size plus thickness */
calc(var(--size) + var(--b))

No, to je tisto, kar nadzoruje razdaljo med črtami v vzorcu. Iz tega lahko naredimo spremenljivko, vendar ni potrebe po dodatni zapletenosti. Sploh ne uporabljam spremenljivke za to v generatorju. Mogoče bom to kasneje spremenil.

Tukaj je isti vzorec, ki gre v drugo smer:

Zagotavljam vam kodo v tej predstavitvi, vendar želim, da jo razčlenite in razumete, katere spremembe sem naredil, da se je to zgodilo.

Poenostavitev kode

V vseh prejšnjih predstavitvah vedno definiramo --size in --p neodvisno. Toda ali se spomnite, kako sem prej omenil, da spletni generator ocenjuje P enako kot m*S, Kjer m nadzoruje ukrivljenost vala? Z definiranjem fiksnega množitelja lahko delamo z enim določenim valom in koda postane lažja. To je tisto, kar bomo potrebovali v večini primerov: določeno valovito obliko in spremenljivko za nadzor njene velikosti.

Posodobimo našo kodo in predstavimo m spremenljivka:

.wave {
  --size: 50px;
  --R: calc(var(--size) * sqrt(var(--m) * var(--m) + 1));

  mask:
    radial-gradient(var(--R) at 50% calc(var(--size) * (1 + var(--m))), #000 99%, #0000 101%) 
      calc(50% - 2*var(--size)) 0/calc(4 * var(--size)) 100%,
    radial-gradient(var(--R) at 50% calc(-1 * var(--size) * var(--m)), #0000 99%, #000 101%) 
      50% var(--size) / calc(4 * var(--size)) 100% repeat-x;
  }

Kot vidite, ne potrebujemo več --p spremenljivka. Zamenjal sem ga z var(--m)*var(--size), in ustrezno optimizirali nekaj matematike. Zdaj, če želimo delati z določeno valovito obliko, lahko izpustimo --m spremenljivko in jo nadomestite s fiksno vrednostjo. Poskusimo .8 npr.

--size: 50px;
--R: calc(var(--size) * 1.28);

mask:
  radial-gradient(var(--R) at 50% calc(1.8 * var(--size)), #000 99%, #0000 101%) 
    calc(50% - 2*var(--size)) 0/calc(4 * var(--size)) 100%,
  radial-gradient(var(--R) at 50% calc(-.8 * var(--size)), #0000 99%, #000 101%) 
    50% var(--size) / calc(4 * var(--size)) 100% repeat-x;

Vidite, kako je koda zdaj lažja? Samo ena spremenljivka za nadzor vašega vala in nanjo se vam ni treba več zanašati sqrt() ki nima podpore za brskalnik!

Enako logiko lahko uporabite za vse predstavitve, ki smo jih videli, tudi za valovite črte in vzorec. Začel sem s podrobno matematično razlago in podal generično kodo, vendar boste morda potrebovali lažjo kodo v resničnem primeru uporabe. To počnem ves čas. Redko uporabljam generično kodo, vendar vedno razmišljam o poenostavljeni različici, zlasti ker v večini primerov uporabljam nekatere znane vrednosti, ki jih ni treba shranjevati kot spremenljivke. (Opozorilo o povezavi: Na koncu bom delil nekaj primerov!)

Omejitve tega pristopa

Matematično bi nam morala koda, ki smo jo naredili, dati popolne valovite oblike in vzorce, v resnici pa se bomo soočili z nekaj čudnimi rezultati. Torej, da, ta metoda ima svoje omejitve. Na primer, spletni generator lahko daje slabe rezultate, zlasti z valovitimi črtami. Del težave je posledica določene kombinacije vrednosti, kjer se rezultat pomeša, na primer uporaba velike vrednosti za debelino roba v primerjavi z velikostjo:

Kako ustvariti valovite oblike in vzorce v CSS PlatoBlockchain Data Intelligence. Navpično iskanje. Ai.
Kako ustvariti valovite oblike in vzorce v CSS

V drugih primerih bo težava povezana z zaokroževanjem, ki bo povzročilo neusklajenost in vrzeli med valovi:

Kako ustvariti valovite oblike in vzorce v CSS PlatoBlockchain Data Intelligence. Navpično iskanje. Ai.
Kako ustvariti valovite oblike in vzorce v CSS

Kljub temu še vedno menim, da metoda, ki smo jo obravnavali, ostaja dobra, ker v večini primerov proizvaja gladke valove, slabim rezultatom pa se zlahka izognemo z igranjem z različnimi vrednostmi, dokler ne postanemo popolne.

Zavijanje

Upam, da po tem članku ne boste več brskali s poskusi in napakami, da bi zgradili valovito obliko ali vzorec. Poleg tega na spletni generator, imate vse matematične skrivnosti za ustvarjanje kakršnega koli vala!

Članek se tukaj konča, zdaj pa imate zmogljivo orodje za ustvarjanje modnih modelov z valovitimi oblikami. Tukaj je navdih za začetek ...

Kaj pa ti? Uporabi moj spletni generator (ali ročno napiši kodo, če si se že naučil vso matematiko na pamet) in mi pokaži svoje stvaritve! Imejmo dobro zbirko v oddelku za komentarje.

Časovni žig:

Več od Triki CSS