0.0.6 • Published 4 years ago

@smotaal.io/dark-mode-controller v0.0.6

Weekly downloads
31
License
MIT
Repository
github
Last release
4 years ago

@smotaal/dark-mode-controller

Reusable controller for Enhanced Dark Mode support via prefers-color-scheme media queries.

Motivation

Integrating a stable user experience for media: prefers-color-scheme can be difficult and can lead to inconsistencies when taming various framework or browser aspects. This controller focuses on the most essential aspects of this UX.

Overview

  • Each controller is immutably constructred for a particular contrainer element which is used to populate any omitted options:

    Note — Wiring only happens when constructed with a container with valid ownerDocument and ownerDocument.defaultView.

    1. Storage:

      TODO — Explore opaque alternative load/save interface meeting the immediacy needs for preventing onload flashing.

      • Optional localStorage for storing enabled or disabled preferences — default defaultView.localStorage.

      • Optional scope for determining the root-relative portion of the storage key — default ownerDocument.location when missing or containing spaces.

      • Optional id fragment appended to the storage key to prevent collisions within a shared scope — default "" when missing or containing spaces.

    2. User Preferences:

      TODO — Explore detecting and adapting to HID and other system-wide preferences.

      • Optional longPressTimeout milliseconds for reverting to auto rounded between 1500 and 10000 — default 2000 when not a positive number.
  • Each controller exposes two hooks for pointer up and down events that can wire into one or more toggler element(s).

    • Exposed methods are automatically bound within the controller so they can be passed to toggler.addEventListener or directly assigned to its onpointerdown or onpointerup.

      Note — For browser compatibility, it might be better for the time being to check if onmousedown and onmouseup exist in the target and use them instead.

  • Code lives in a single ECMAScript module for direct use for supporting runtimes.

  • Type definitions are included for TypeScript versions that support jsdoc style annotations.

Intended Uses

Native

Let's assume you are loading ESM files directly in your browser:

// Make sure you to adjust based on your own tooling
import {DarkModeController} from '/controllers/dark-mode-controller.js';

// Make sure you create this element first
const darkModeToggle = document.getElementById('dark-mode-toggle');

const darkModeController = new DarkModeController(document.documentElement);

// Use mouse events if present (until browsers get caught up)
darkModeToggle['onmousedown' in darkModeToggle ? 'onmousedown' : 'onpointerdown'] = darkModeController.onPointerDown;
darkModeToggle['onmouseup' in darkModeToggle ? 'onmouseup' : 'onpointerup'] = darkModeController.onPointerUp;

Frameworks

Let's assume you are using TypeScript and have it configured for JSX:

// Figure out how to import the actual framework based following
//   their best practices and conventions.
import Framework from 'Framework';

// Any framework that supports adding per specs event handlers
//   passing the `Event { type: 'pointerdown' | 'pointerup' }`
//   as a single or first argument.

// Figure out how to import this module based on your setup/tooling
import { DarkModeController } from '@smotaal/dark-mode-controller';

// You may need to do type coercion for the handlers in your preferred
//   way, but if all else fails, consider using an interface like:
interface FrameworkDarkModeController extends DarkModeController {
  onPointerDown(event: Framework.PointerEvent | PointerEvent): void;
  onPointerUp(event: Framework.PointerEvent | PointerEvent): void;
}

// Now you can create a coerced instance like so:

const darkModeController =
  (new DarkModeController() as any) as FrameworkDarkModeController;

// Finally, you may want to consider how you're exposing this
//   based on your project's structure and styles:
//
//   - You can export a singleton and wire it to components elsewhere:
export default darkModeController;
//
//   - Or export a component that wraps the singleton:
//
export const DarkModeToggle = () => (
  <button
    onPointerDown={darkModeController.onPointerDown}
    onPointerUp={darkModeController.onPointerUp}
  />
);

// NOTE: If you prefer to wire to the `Framework.MouseEvents`,
//   those should work and you can adjust the type definitions
//   to account for `onMouseDown{….onPointerDown}`… etc.

Note — If you got it working with one or more framework(s), I'd be happy to know how easy or hard it was to do. I'm also interested to try to keep those instructions as minimal as possible, so I am open to making improvements as long as they are relevant and universally suited.


If you find dark-mode-contoller suitable for your particular case, please don't hesitate to contribute to this project. If not, please let me know why.

0.0.6

4 years ago

0.0.5

4 years ago

0.0.4

4 years ago

0.0.3

5 years ago

0.0.2

5 years ago