Skip to content

Commit

Permalink
New: Theme Mode Svelte
Browse files Browse the repository at this point in the history
  • Loading branch information
1aron committed Mar 3, 2024
1 parent 079379e commit 1122356
Show file tree
Hide file tree
Showing 24 changed files with 996 additions and 53 deletions.
4 changes: 4 additions & 0 deletions packages/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export default class ThemeMode {
private _darkMQL?: MediaQueryList
private _preference?: ThemePreference
private _value?: ThemeValue
mounted = false

constructor(
public options?: Options,
Expand All @@ -14,6 +15,7 @@ export default class ThemeMode {
}

init() {
this.mounted = true
if (!this.host) this.host = document.documentElement
this._darkMQL = matchMedia('(prefers-color-scheme:dark)')
const storage = this.storage
Expand All @@ -34,6 +36,7 @@ export default class ThemeMode {
}

set preference(preference: ThemePreference) {
if (!this.mounted) return
if (preference !== this._preference) {
if (preference === 'system') {
this._darkMQL?.addEventListener?.('change', this._onThemeChange)
Expand All @@ -54,6 +57,7 @@ export default class ThemeMode {
}

set value(value: ThemeValue) {
if (!this.mounted) return
const previous = this._value
this._value = value
if (this.host && previous !== value) {
Expand Down
4 changes: 2 additions & 2 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@
"theme-mode": "workspace:^"
},
"devDependencies": {
"@playwright/experimental-ct-react": "1.41.0",
"@playwright/test": "1.41.0",
"@playwright/experimental-ct-react": "1.41.2",
"@playwright/test": "1.41.2",
"eslint-plugin-react-hooks": "^4.6.0"
}
}
56 changes: 56 additions & 0 deletions packages/svelte/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<br>
<div align="center">

<p align="center">
<a href="https://css.master.co">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/33840671/201701649-3bb7d698-abec-4d5f-ac30-ccc4d7bafcd4.svg">
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/33840671/201703010-77bf2373-9899-40cc-98f5-30cf9b546941.svg">
<img alt="Master CSS" src="https://user-images.githubusercontent.com/33840671/201703010-77bf2373-9899-40cc-98f5-30cf9b546941.svg" width="100%">
</picture>
</a>
</p>
<p align="center">Integrate Master CSS in Svelte way</p>

<p align="center">
<a aria-label="GitHub release (latest by date including pre-releases)" href="https://github.com/master-co/css/releases">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/github/v/release/master-co/css?include_prereleases&color=212022&label=&style=for-the-badge&logo=github&logoColor=fff">
<source media="(prefers-color-scheme: light)" srcset="https://img.shields.io/github/v/release/master-co/css?include_prereleases&color=f6f7f8&label=&style=for-the-badge&logo=github&logoColor=%23000">
<img alt="NPM Version" src="https://img.shields.io/github/v/release/master-co/css?include_prereleases&color=f6f7f8&label=&style=for-the-badge&logo=github">
</picture>
</a>
<a aria-label="NPM Package" href="https://www.npmjs.com/package/@master/css.svelte">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/npm/dm/@master/css.svelte?color=212022&label=%20&logo=npm&style=for-the-badge">
<source media="(prefers-color-scheme: light)" srcset="https://img.shields.io/npm/dm/@master/css.svelte?color=f6f7f8&label=%20&logo=npm&style=for-the-badge">
<img alt="NPM package ( download / month )" src="https://img.shields.io/npm/dm/@master/css.svelte?color=f6f7f8&label=%20&logo=npm&style=for-the-badge">
</picture>
</a>
<a aria-label="Discord Community" href="https://discord.gg/sZNKpAAAw6">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/discord/917780624314613760?color=212022&label=%20&logo=discord&style=for-the-badge">
<source media="(prefers-color-scheme: light)" srcset="https://img.shields.io/discord/917780624314613760?color=f6f7f8&label=%20&logo=discord&style=for-the-badge">
<img alt="Discord online" src="https://img.shields.io/discord/917780624314613760?color=f6f7f8&label=%20&logo=discord&style=for-the-badge">
</picture>
</a>
<a aria-label="Follow @mastercorg" href="https://twitter.com/mastercorg">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/static/v1?label=%20&message=twitter&color=212022&logo=twitter&style=for-the-badge">
<source media="(prefers-color-scheme: light)" srcset="https://img.shields.io/static/v1?label=%20&message=twitter&color=f6f7f8&logo=twitter&style=for-the-badge">
<img alt="Follow @mastercorg" src="https://img.shields.io/static/v1?label=%20&message=twitter&color=f6f7f8&logo=twitter&style=for-the-badge">
</picture>
</a>
<a aria-label="Github Actions" href="https://github.com/master-co/css/actions/workflows/release.yml">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/github/actions/workflow/status/master-co/css/release.yml?branch=rc&label=%20&message=twitter&color=212022&logo=githubactions&style=for-the-badge">
<source media="(prefers-color-scheme: light)" srcset="https://img.shields.io/github/actions/workflow/status/master-co/css/release.yml?branch=rc&label=%20&message=twitter&color=f6f7f8&logo=githubactions&style=for-the-badge&logoColor=%23000">
<img alt="Github release actions" src="https://img.shields.io/github/actions/workflow/status/master-co/css/release.yml?branch=rc&label=%20&message=twitter&color=f6f7f8&logo=githubactions&style=for-the-badge&logoColor=%23000">
</picture>
</a>
</p>

</div>

## Documentation
Check out the official [documentation](https://rc.css.master.co/docs/installation/svelte).
8 changes: 8 additions & 0 deletions packages/svelte/e2e/App.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script lang="ts">
import ThemeModeProvider from "../src/lib";
import HelloWorld from "./HelloWorld.svelte";
</script>

<ThemeModeProvider options={{ preference: "system" }}>
<HelloWorld />
</ThemeModeProvider>
22 changes: 22 additions & 0 deletions packages/svelte/e2e/App.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { test, expect } from '@playwright/experimental-ct-svelte'
import App from './App.svelte'

test('theme-mode', async ({ mount, page }) => {
const app = await mount(App)

// init
await Promise.all([
expect(app.locator('#value')).toHaveText('light'),
expect(app.locator('#preference')).toHaveText('system'),
expect(await page.locator('html').getAttribute('class')).toBe('light')
])

// change preference
await app.locator('select').selectOption('dark')
await Promise.all([
expect(app.locator('#value')).toHaveText('dark'),
expect(app.locator('#preference')).toHaveText('dark'),
expect(await page.evaluate(() => localStorage.getItem('theme-preference'))).toBe('dark'),
expect(await page.locator('html').getAttribute('class')).toBe('dark')
])
})
15 changes: 15 additions & 0 deletions packages/svelte/e2e/HelloWorld.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script lang="ts">
import { getThemeMode } from "../src/lib";
const themeMode = getThemeMode();
</script>

<span id="value">{$themeMode.value}</span>
<span id="preference">{$themeMode.preference}</span>
<select
class="abs full inset:0 opacity:0"
bind:value={$themeMode.preference}
>
<option value="light">☀️ Light</option>
<option value="dark">🌜 Dark</option>
<option value="system">System</option>
</select>
27 changes: 27 additions & 0 deletions packages/svelte/e2e/playwright-ct.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { defineConfig, devices } from '@playwright/experimental-ct-svelte'

export default defineConfig({
testDir: './',
timeout: 15000,
fullyParallel: true,
forbidOnly: !!process.env.CI,
workers: process.env.CI ? 1 : undefined,
reporter: 'list',
use: {
ctPort: 5103
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
// {
// name: 'firefox',
// use: { ...devices['Desktop Firefox'] },
// },
// {
// name: 'webkit',
// use: { ...devices['Desktop Safari'] },
// },
],
})
12 changes: 12 additions & 0 deletions packages/svelte/e2e/playwright/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Testing Page</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="./index.ts"></script>
</body>
</html>
Empty file.
18 changes: 18 additions & 0 deletions packages/svelte/e2e/svelte.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import adapter from '@sveltejs/adapter-auto'
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'

/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),

kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter()
}
}

export default config
78 changes: 78 additions & 0 deletions packages/svelte/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{
"name": "@master/theme-mode.svelte",
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build && pnpm run package",
"preview": "vite preview",
"package": "svelte-kit sync && svelte-package && publint",
"prepublishOnly": "pnpm run package",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"e2e": "playwright test -c e2e/playwright-ct.config.ts"
},
"license": "MIT",
"description": "A lightweight utility for switching CSS theme modes",
"author": "Aoyue Design LLC.",
"funding": "https://css.master.co/docs/donate",
"homepage": "https://css.master.co",
"bugs": {
"url": "https://github.com/master-co/theme-mode/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/master-co/theme-mode.git",
"directory": "packages/svelte"
},
"keywords": [
"theme-mode",
"component",
"element",
"provider",
"svelte",
"hooks",
"use",
"css",
"mastercss",
"server-component",
"client-component"
],
"sideEffects": false,
"svelte": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"svelte": "./dist/index.js"
}
},
"files": [
"dist",
"!dist/**/*.test.*",
"!dist/**/*.spec.*"
],
"publishConfig": {
"access": "public",
"provenance": true
},
"peerDependencies": {
"svelte": "^4.0.0"
},
"dependencies": {
"theme-mode": "workspace:^"
},
"devDependencies": {
"@playwright/experimental-ct-svelte": "1.41.2",
"@playwright/test": "1.41.2",
"@sveltejs/adapter-auto": "^3.1.1",
"@sveltejs/kit": "^2.5.2",
"@sveltejs/package": "^2.2.7",
"@sveltejs/vite-plugin-svelte": "^3.0.2",
"publint": "^0.1.16",
"svelte": "^4.2.12",
"svelte-check": "^3.6.6",
"tslib": "^2.6.2",
"typescript": "^5.3.3",
"vite": "^5.1.4"
}
}
13 changes: 13 additions & 0 deletions packages/svelte/src/app.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}

export {};
12 changes: 12 additions & 0 deletions packages/svelte/src/app.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div>%sveltekit.body%</div>
</body>
</html>
33 changes: 33 additions & 0 deletions packages/svelte/src/lib/ThemeModeProvider.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<script lang="ts">
import { onMount, onDestroy, setContext } from "svelte";
import { writable } from "svelte/store";
import ThemeMode, { type Options } from "theme-mode";
export let options: Options | undefined = undefined;
export let host: HTMLElement | undefined = undefined;
const themeMode = writable(new ThemeMode(options, host));
const onThemeModeChange = () => themeMode.set($themeMode);
onMount(() => {
$themeMode.init();
themeMode.update(() => $themeMode);
$themeMode.host?.addEventListener(
"themeModeChange",
onThemeModeChange,
{ passive: true },
);
});
onDestroy(() => {
$themeMode.destroy();
$themeMode.host?.removeEventListener(
"themeModeChange",
onThemeModeChange,
);
});
setContext("theme-mode", themeMode);
</script>

<slot />
7 changes: 7 additions & 0 deletions packages/svelte/src/lib/get-theme-mode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { getContext } from 'svelte'
import type { Writable } from 'svelte/store'
import type ThemeMode from 'theme-mode'

export function getThemeMode() {
return getContext<Writable<ThemeMode>>('theme-mode')
}
4 changes: 4 additions & 0 deletions packages/svelte/src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// export { cssRuntimeSymbol, getCSSRuntime } from './theme-mode'
export * from './ThemeModeProvider.svelte'
export * from './get-theme-mode'
export { default } from './ThemeModeProvider.svelte'
8 changes: 8 additions & 0 deletions packages/svelte/src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script lang="ts">
import ThemeModeProvider from '../lib/ThemeModeProvider.svelte';
import HelloWorld from './HelloWorld.svelte';
</script>

<ThemeModeProvider options={{ preference: "system" }}>
<HelloWorld />
</ThemeModeProvider>
12 changes: 12 additions & 0 deletions packages/svelte/src/routes/HelloWorld.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script lang="ts">
import { getThemeMode } from "../lib";
const themeMode = getThemeMode();
</script>

<span id="value">{$themeMode.value}</span>
<span id="preference">{$themeMode.preference}</span>
<select class="abs full inset:0 opacity:0" bind:value={$themeMode.preference}>
<option value="light">☀️ Light</option>
<option value="dark">🌜 Dark</option>
<option value="system">System</option>
</select>
Binary file added packages/svelte/static/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 1122356

Please sign in to comment.