Weblog

Uudistin Abbletin markkinointisivuston, jotta Abbletista olisi helpompi ymmärtää, mistä siinä on kyse. Onko uusi sivu selkeä? Haluaisin kuulla, mikä siinä jää vielä epäselväksi.
Kuvakaappaus Abbletin uudistetusta markkinointisivustosta, jossa näkyvät etusivu ja ominaisuuksien esittelyt.

Älykodin päivitys: Hue ja IKEA toimimaan yhdessä

Philips Hue on toiminut kotona hyvin, mutta älyvalojen ja kytkimien lisääminen tulee kalliiksi. Siksi halusin siirtyä vähitellen edullisempiin IKEA-laitteisiin.

Kotona oli jo pieni palvelin: Ubuntuun asennettu Mac mini. Asensin siihen virtuaalikoneeseen Home Assistant OS:n, jotta voisin ohjata kodin laitteita yhdestä paikasta ja yhdistellä eri valmistajien tuotteita vapaammin.

Ajattelin ensin, että IKEAan siirtyminen tarkoittaisi Hue-laitteista luopumista. Kävi ilmi, ettei niin tarvinnut tehdä. ConBee III -koordinaattorin ja Home Assistantin ZHA-integraation avulla sain Hue-lamppuja liitettyä järjestelmään suoraan ilman Hue Bridgeä. Myös IKEA-kytkimillä pystyi ohjaamaan Hue-lamppuja.

Se osoittautui paremmaksi ratkaisuksi kuin kaikkien laitteiden vaihtaminen saman merkin tuotteisiin. Voin lisätä edullisempia IKEA-laitteita sinne, missä niistä on hyötyä, ja käyttää edelleen omistamiani Hue-lamppuja. Home Assistant kokoaa laitteet samaan järjestelmään.

Siirsin laitteita järjestelmään vähitellen, yhden kerrallaan. Samalla pystyin kokeilemaan automaatioita ja tarkistamaan niiden tapahtumajälkiä, jos jokin ei toiminut odotetusti. Kun kytkin kerran lakkasi reagoimasta, jäljistä näkyi, että Home Assistant oli vastaanottanut painalluksen ja lähettänyt ohjauskomennot. Häiriön tarkka syy ei selvinnyt, mutta lokit auttoivat rajaamaan, missä kohtaa ongelmaa ei ollut.

Tavoitteena oli säästää siirtymällä Huesta edullisempiin IKEA-laitteisiin. Home Assistantin avulla sainkin tuotua molemmat merkit yhteen. Nyt voin valita laitteen tarpeen ja hinnan mukaan, eikä koko kodin tarvitse käyttää samaa merkkiä.

Vanha Mac mini kotipalvelimena – käynnistymään sähkökatkon jälkeen

Minulla on vuoden 2014 Intel Mac mini kotipalvelimena, ja se pyörittää Ubuntu Serveriä. Sähkökatkon jälkeen kone ei kuitenkaan käynnistynyt itsestään, kun virta palautui.

Ratkaisu löytyi Intelin 8-sarjan piirisarjan virranhallinta-asetuksesta. GEN_PMCON_3-rekisterin AFTERG3_EN-bitti määrää, käynnistyykö järjestelmä uudelleen virran palauduttua. Intelin dokumentaation mukaan bitin arvo 0 palauttaa järjestelmän käyntiin ja arvo 1 jättää sen sammutettuun tilaan.

Apple listaa vuoden 2014 Mac mini -kokoonpanot mallina Macmini7,1. Alla käytetty PCI-osoite 00:1f.0 vastaa Intelin piirisarjadokumentaation kohtaa D31:F0. Muutin asetuksen Ubuntu Serverissä tällä komennolla:

sudo setpci -s 00:1f.0 0xa4.b=0:1

Komento tyhjentää bitin 0 kyseisen laitteen 0xa4-rekisteristä. Sen jälkeen katkaisin virran ja palautin sen. Mac mini käynnistyi automaattisesti.

Testasin tätä yhdessä vuoden 2014 Mac mini -mallissa (Macmini7,1), en kaikissa malliperheen kokoonpanoissa. Älä sovella ohjetta suoraan muihin Mac mini -sukupolviin, joissa piirisarja tai rekisterien osoitteet voivat olla erilaisia. Tarkista ensin tarkka malli ja sen piirisarjan dokumentaatio. Tekoälyltä voi pyytää apua tietojen etsimiseen, mutta varmista ehdotetun rekisterin ja komennon oikeellisuus luotettavasta lähteestä ennen ajamista.

Intel: 8 Series/C220 Series -piirisarjan dokumentaatio

Näkyvän viewportin korkeus CSS-muuttujana

Koko näytön web-sovelluksessa layoutin pitäisi mahtua siihen alueeseen, joka on juuri nyt näkyvissä. Mobiiliselaimessa tämä alue muuttuu, kun selaimen palkit liikkuvat tai näppäimistö avautuu. Erityisesti iOS:llä näppäimistö voi pienentää näkyvää viewportia niin, että näkymän alaosan sisältö jää muuten sen alle.

window.visualViewport.height kertoo näkyvän viewportin korkeuden CSS-pikseleinä. Kun arvoa seurataan resize-tapahtumissa, sovellus voi välittää mitan CSS:lle mukautettua layoutia varten.

function updateVisibleViewportHeight() {
  const height = window.visualViewport?.height ?? window.innerHeight;
  document.documentElement.style.setProperty("--vvh", `${height / 100}px`);
}

updateVisibleViewportHeight();
window.visualViewport?.addEventListener("resize", updateVisibleViewportHeight);
window.addEventListener("resize", updateVisibleViewportHeight);

Esimerkissä korkeus jaetaan sadalla ennen kuin se asetetaan --vvh-muuttujaan. Muuttuja vastaa silloin yhtä prosenttia näkyvän viewportin korkeudesta. CSS voi kertoa sen sadalla ja käyttää tulosta esimerkiksi koko näkymän vähimmäiskorkeutena:

.full-screen-view {
  min-height: calc(var(--vvh) * 100);
}

JavaScript huolehtii mittaamisesta, ja CSS määrittää, miten layout käyttää mittaa. Sama ajatus sopii PWA:n mobiililayoutiin, kun sisällön pitää reagoida näkyvän alueen muutoksiin.

Tein Faunder UI:hin ensimmäisen version tummasta tilasta. Kuvassa ovat Button Types -osion painikkeiden esikatselu ja HTML-esimerkki. Tämä on ensimmäinen kokeilu siitä, miltä dokumentaation esimerkit näyttävät tummalla taustalla. Lisää dokumentaatiosta: ui.faunder.fi
Faunder UI:n Button Types -osion painikkeiden esikatselu ja HTML-esimerkki tummalla taustalla. Kuvan alareunassa ui.faunder.fi.
Four years ago, I was sketching out a minimalist weblog. I saved the idea and eventually forgot about it. Now, while building Faunder’s new homepage, I finally found the right place for it: the weblog. Funny how an old idea can wait for the right moment.
Screenshot of an early minimalist weblog concept, showing a centered stream of images on a white page.
Meillä on uusi verkkosivu!
Kuvakaappaus Faunderin uudesta etusivusta, jossa esitellään Abblet, Cuukbuuk ja Faunder UI.

Yhden tiedoston Preact-komponentit

Yksi asia, josta olen aina pitänyt Vue:ssa, on sen Single-File Component (SFC) -lähestymistapa. Yhdessä .vue-tiedostossa logiikka, templaatti ja tyylit asuvat rinnakkain. Kaikki, mikä kuuluu komponenttiin, on samassa paikassa — komponentteja on helppo ymmärtää, refaktoroida ja siirtää.

Halusin saman tunteen Preactilla työskennellessäni.

Preact ei määrittele valmiiksi Vue-tyylistä yhden tiedoston formaattia, mutta se on tarpeeksi joustava omiin käytäntöihin. Tämä artikkeli kuvaa omaa tapaani rakentaa yhden tiedoston Preact-komponentteja Vue SFC:istä inspiroituneena — ilman build-vaihetta, kääntäjiä tai framework-tason abstraktioita.

Tavoite

  • Pidä logiikka, markup ja tyylit yhdessä tiedostossa
  • Vältä monimutkaista työkaluketjua tai build-vaihetta
  • Pysy idiomaattisessa Preactissa
  • Tee komponenteista helppolukuisia ja siirrettäviä

Ydinajatus

Yhden tiedoston Preact-komponentti sisältää tavallisen Preact-funktiokomponentin, sen markupin ja tyylit vierekkäin.

Ainoat omat apurit ovat pienet html- ja css-templaattitagit. Sen enempää magiaa ei tarvita.

Minimaalinen esimerkki

// ExampleComponent.ts
import { html, css } from "@faunder/markup"
import { useState } from "preact/hooks"

export function MyComponent() {
  const [count, setCount] = useState(0)

  const view = html`
    <div data-scope="MyComponent">
      <h1>Hello World</h1>
      <p>Count: ${count}</p>
      <button onClick=${() => setCount((c) => c + 1)}>
        Increment
      </button>
    </div>
  `

  const style = css`
    @scope ([data-scope="MyComponent"]) to ([data-scope]) {
      h1 {
        color: var( --primary-600);
      }
    }
  `

  return [view, style]
}

The css Helper

css-apuri on tarkoituksella yksinkertainen. Se on template tag, joka palauttaa raakaa CSS:ää sisältävän <style>-elementin. Pidän nämä pienet apurit Faunder Markupissa, johon on linkki alempana.

There's no runtime, no hashing, and no transformation step. What you write is what ends up in the DOM.

Miksi pidän tästä

  • Kolokaatio — logiikka, markup ja tyylit samassa paikassa
  • Yksinkertaisuus — ei custom build-putkia tai kääntäjiä
  • Ennustettavuus — tavallisia Preact-komponentteja
  • Helppo refaktorointi — siirrä tai poista komponentti yhtenä tiedostona

Tuntuma on lähellä Vue SFC -filosofiaa, mutta toteutus on kokonaan käyttäjätason käytäntöä.

Lopuksi

Preact ei pakota tiettyyn komponenttimuotoon — ja se on vahvuus. Pienillä apureilla ja modernilla CSS:llä saat Vue SFC:ien parhaat puolet Preact-ekosysteemissä.

Ladattavat

Moderni CSS Scope

Moderni CSS tuo @scope-säännön, joka rajaa tyylit tiettyihin elementteihin. Ratkaisu sopii komponenttipohjaiseen tyylittelyyn ilman build-vaihetta tai CSS-in-JS -runtimea.

@scope määrittelee rajauksen juuren ja valinnaisen ylärajan. Tyylit koskevat vain rajauksen sisällä olevia elementtejä — ihanteellinen komponenttipohjaiseen ajatteluun.

Perusrajaukset

Yksinkertaisin tapaus: rajaa tyylit komponenttiin data-scope-attribuutilla:

<!-- HTML -->
<div data-scope="Card">
  <h1>Title</h1>
  <p>Content here</p>
</div>

/* CSS */
@scope ([data-scope="Card"]) to ([data-scope]) {
  h1 {
    color: var( --primary-600);
  }
  p {
    margin-bottom: 1rem;
  }
}

@scope ([data-scope="Card"]) varmistaa, että tyylit koskevat vain kyseistä komponenttia. to ([data-scope]) asettaa ylärajan ja estää tyylien vuotamisen sisäkkäisiin komponentteihin.

Sisäkkäiset komponentit

Sisäkkäisissä komponenteissa rajaus estää parent-tyylit vaikuttamasta lapsiin:

<!-- HTML -->
<div data-scope="Article">
  <h1>Article Title</h1>
  <div data-scope="Card">
    <h1>Card Title</h1>
    <p>Card content</p>
  </div>
</div>

/* CSS */
@scope ([data-scope="Article"]) to ([data-scope]) {
  h1 {
    color: var( --primary-600);
    font-size: 2rem;
  }
}

@scope ([data-scope="Card"]) to ([data-scope]) {
  h1 {
    color: var(--neutral-700);
    font-size: 1.5rem;
  }
}

Tässä esimerkissä Article-tyylit eivät vaikuta Card-komponenttiin to ([data-scope]) -rajan ansiosta. Jokainen komponentti pitää omat tyylinsä erillään.

Hyödyt

  • Ei runtime-kuormaa – Tyylit ovat tavallista CSS:ää, jonka selain parsii
  • Ei build-vaihetta – Toimii vanilla HTML/CSS:n kanssa ja minkä tahansa build-työkalun kanssa
  • Automaattinen eristys – Rajaus estää tyylien vuotamisen
  • Selaimen natiivi – Moderni CSS, jota kaikki nykyselaimet tukevat

Selaintuki

@scope on nyt tuettu kaikissa moderneissa selaimissa. Firefox lisäsi tuen joulukuussa 2025, jolloin tuki täyttyi.

Lopuksi

CSS @scope on tehokas, nollaruntime-ratkaisu komponenttipohjaiseen tyylittelyyn. Saat rajatut tyylit ilman CSS-in-JS-kirjastojen tai build-aikaisten muunnosten monimutkaisuutta.