Weblog

I’ve reworked Abblet’s marketing site to explain more clearly what Abblet is. Does the new page make sense? I’d like to hear what still feels unclear.
Screenshot of Abblet's redesigned marketing site, showing its homepage and feature sections.

Smart Home Update: Bringing Hue and IKEA Together

Philips Hue has worked well at home, but adding more smart lights and switches can get expensive. I wanted to gradually move to more affordable IKEA devices.

I already had a small home server: a Mac mini running Ubuntu. I installed Home Assistant OS in a virtual machine on it, giving me one place to control devices and more freedom to combine products from different brands.

At first, I thought switching to IKEA would mean giving up my Hue devices. It turned out that wasn’t necessary. Using a ConBee III coordinator with Home Assistant’s ZHA integration, I was able to connect Hue lights directly to the system without the Hue Bridge. IKEA switches could also control Hue lights.

That turned out to be a better solution than replacing everything with one brand. I can add more affordable IKEA devices where they make sense and keep using the Hue lights I already own. Home Assistant brings them together in one system.

I moved the devices over gradually, one at a time. That also gave me a chance to try out automations and check their traces when something didn’t work as expected. When a switch stopped responding once, the traces showed that Home Assistant had received the button press and sent the control commands. I couldn’t confirm the exact cause of the interruption, but the logs helped narrow down where the problem wasn’t.

The goal was to save money by moving from Hue to the more affordable IKEA range. With Home Assistant, I ended up bringing both brands together instead. Now I can choose devices based on what fits the need and the budget, without making the whole home use one brand.

Mac mini as a Home Server: Restarting After a Power Outage

I use a 2014 Intel Mac mini as a home server running Ubuntu Server. After a power outage, it did not start automatically when power came back.

The fix was in a power-management setting on the Intel 8 Series chipset. The AFTERG3_EN bit in the GEN_PMCON_3 register controls what the system does when power returns after a complete power loss. Intel's documentation says a value of 0 boots the system when power returns, while a value of 1 leaves it in the soft-off state.

Apple identifies the Late 2014 Mac mini configurations as Macmini7,1. The PCI address used below, 00:1f.0, corresponds to D31:F0 in Intel's chipset documentation. I changed the setting on Ubuntu Server with this command:

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

The command clears bit 0 in register 0xa4 for that chipset function. I then cut the power and restored it. The Mac mini started automatically.

I tested this on one Late 2014 Mac mini (Macmini7,1); I have not tested every configuration in that model family. Do not apply it directly to other Mac mini generations, which may use different chipsets or register mappings. Check the exact model and its chipset documentation first. An AI tool can help find relevant information, but verify any suggested register and command against a reliable source before running it.

Intel: 8 Series/C220 Series chipset documentation

The Visible Viewport Height as a CSS Variable

In a full-screen web app, the layout should fit the area that is currently visible. On mobile browsers, this area changes as browser controls move or the keyboard opens. On iOS in particular, the keyboard can reduce the visible viewport and cover content at the bottom of the screen.

window.visualViewport.height gives the height of the visible viewport in CSS pixels. By tracking it in resize events, an app can pass the measurement to CSS and adapt its layout.

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);

In this example, the height is divided by 100 before it is assigned to the --vvh variable. The variable then represents one percent of the visible viewport height. CSS can multiply it by 100 and use the result, for example, as the minimum height of a full-screen view:

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

JavaScript measures the viewport, and CSS determines how the layout uses that measurement. The same approach can be used in a PWA mobile layout when content needs to respond to changes in the visible area.

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.
We have a new website!
Screenshot of Faunder's new website homepage, with links to Abblet, Cuukbuuk, and Faunder UI.

Single-File Preact Components

One thing I've always loved about Vue is its Single-File Component (SFC) approach. In a single .vue file, you colocate JavaScript logic, template markup, and CSS styles. Everything that belongs to a component lives together, which makes components easy to reason about, refactor, and move around.

I wanted that same feeling when working with Preact.

Preact doesn't prescribe a single-file format like Vue does, but it's flexible enough that you can build your own conventions. This article describes my own approach to single-file Preact components, inspired by Vue SFCs, without introducing a build step, custom compilers, or framework-level abstractions.

The Goal

  • Keep component logic, markup, and styles in one file
  • Avoid complex tooling or build steps
  • Stay close to idiomatic Preact
  • Make components easy to read and move

The Core Idea

A single-file Preact component in this style contains a normal Preact function component, its markup, and its styles defined right next to each other.

The only custom utilities involved are small html and css template tags. There's no magic beyond that.

Minimal Example

// 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

The css helper is intentionally boring. It's just a template tag that returns a <style> node containing raw CSS. I keep these small helpers in Faunder Markup, linked below.

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

Why I Like This Approach

  • Colocation – logic, markup, and styles live together
  • Simplicity – no custom build pipeline or compiler
  • Predictability – plain Preact components
  • Easy refactoring – move or delete a component as a single file

It feels very close to Vue's SFC philosophy, but implemented entirely at the user-land level.

Closing Thoughts

Preact doesn't force you into a specific component format — and that's a strength. With small helpers and modern CSS, you can recreate much of what makes Vue SFCs enjoyable, while staying fully within the Preact ecosystem.

Downloads

Modern CSS Scope

Modern CSS introduces the @scope rule, which provides a powerful way to scope styles to specific elements. This creates an elegant solution for component-based styling without build-time processing or CSS-in-JS runtime overhead.

The @scope rule allows you to define a scoping root and an optional scoping limit. Styles within the scope only apply to elements within that boundary, making it perfect for component-based thinking and modular HTML structures.

Basic Scoping

The simplest use case is scoping styles to a component using a data-scope attribute:

<!-- 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;
  }
}

The @scope ([data-scope="Card"]) rule ensures that all styles within it only apply to elements inside the component with that scope attribute. The to ([data-scope]) part creates a scoping limit, preventing styles from leaking into nested components.

Nested Components

When components are nested, the scoping limit prevents parent styles from affecting child components:

<!-- 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;
  }
}

In this example, styles scoped to Article won't affect the Card component because of the to ([data-scope]) limit. Each component maintains its own style isolation.

Benefits

  • No runtime overhead – Styles are plain CSS, parsed by the browser
  • No build step required – Works with vanilla HTML/CSS and any build tool
  • Automatic isolation – Scoping limits prevent style leakage
  • Browser-native – Uses modern CSS features supported by all modern browsers

Browser Support

The @scope rule is now supported in all modern browsers. Firefox added support on December 2025, completing the browser coverage.

Closing Thoughts

CSS @scope creates a powerful, zero-runtime styling solution for component-based thinking. It brings the benefits of scoped styles without the complexity of CSS-in-JS libraries or build-time CSS processing. With native browser support, it's ready to use in any modern web project.