bluedither 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,88 @@
1
+ # React Generation
2
+
3
+ When the target project uses **React** (detected via `react` in package.json dependencies), follow these patterns.
4
+
5
+ ## Detection
6
+
7
+ React is detected when `package.json` contains `react` in `dependencies` or `devDependencies`. Also check for:
8
+ - `react-dom` (standard React)
9
+ - `next` (Next.js — use `"use client"` directive)
10
+ - TypeScript: present if `typescript` is in devDependencies or `tsconfig.json` exists
11
+
12
+ ## Component Structure
13
+
14
+ Generate separate component files matching `structure.json` nodes:
15
+
16
+ ### `BlueDitherTheme.jsx` (or `.tsx`)
17
+ Root component wrapping the full layout. Imports and composes child components.
18
+
19
+ ```jsx
20
+ import { BlueDitherHeader } from './BlueDitherHeader';
21
+ import { BlueDitherHero } from './BlueDitherHero';
22
+ import './bluedither.css';
23
+
24
+ export function BlueDitherTheme() {
25
+ return (
26
+ <div className="bd-root">
27
+ <BlueDitherHero />
28
+ <BlueDitherHeader />
29
+ </div>
30
+ );
31
+ }
32
+ ```
33
+
34
+ ### `BlueDitherHeader.jsx`
35
+ Navbar component with logo, nav items, and CTA.
36
+
37
+ - Nav items come from `content.navItems` token array
38
+ - Use `<nav>` with `<a>` elements for nav items
39
+ - CTA is an `<a>` or `<button>` element
40
+
41
+ ### `BlueDitherHero.jsx`
42
+ Hero section with shader and text content.
43
+
44
+ - **Shader initialization**: Use `useEffect` + `useRef` for the shader parent div:
45
+ ```jsx
46
+ import { useEffect, useRef } from 'react';
47
+
48
+ const shaderRef = useRef(null);
49
+
50
+ useEffect(() => {
51
+ // Dynamic import to avoid SSR issues
52
+ import('./paper-shaders-bundle.js').then(({ ShaderMount, ditheringFragmentShader, getShaderColorFromString }) => {
53
+ const mount = new ShaderMount(shaderRef.current, ditheringFragmentShader, uniforms, opts, speed, 0, 2);
54
+ return () => mount.dispose();
55
+ });
56
+ }, []);
57
+ ```
58
+
59
+ ### `bluedither.css`
60
+ All CSS custom properties and `.bd-*` class rules, identical to the vanilla `<style>` block but as an external file.
61
+
62
+ ## TypeScript
63
+
64
+ If TypeScript is detected, use `.tsx` extensions and add minimal type annotations:
65
+ - Props interfaces where components accept props
66
+ - `useRef<HTMLDivElement>(null)` for refs
67
+
68
+ ## Next.js
69
+
70
+ If Next.js is detected:
71
+ - Add `"use client"` at the top of components that use `useEffect` or `useRef`
72
+ - The CSS file can be imported directly in the component
73
+
74
+ ## File Output
75
+
76
+ | File | Contents |
77
+ |------|----------|
78
+ | `BlueDitherTheme.jsx/tsx` | Root layout component |
79
+ | `BlueDitherHeader.jsx/tsx` | Navbar component |
80
+ | `BlueDitherHero.jsx/tsx` | Hero + shader component |
81
+ | `bluedither.css` | CSS custom properties + rules |
82
+ | `paper-shaders-bundle.js` | Shader library (copied) |
83
+
84
+ ## Content Handling
85
+
86
+ - Inject content tokens directly as JSX text
87
+ - Headline newlines: use `{headline.split('\\n').map((line, i) => <Fragment key={i}>{i > 0 && <br />}{line}</Fragment>)}`
88
+ - Nav items: `{navItems.map(item => <a key={item} className="bd-nav-item" href="#">{item}</a>)}`
@@ -0,0 +1,103 @@
1
+ # Svelte Generation
2
+
3
+ When the target project uses **Svelte** (detected via `svelte` in package.json dependencies), follow these patterns.
4
+
5
+ ## Detection
6
+
7
+ Svelte is detected when `package.json` contains `svelte` in `dependencies` or `devDependencies`. Also check for:
8
+ - `@sveltejs/kit` (SvelteKit)
9
+ - TypeScript: present if `typescript` is in devDependencies or `svelte.config.js` references TypeScript
10
+
11
+ ## Component Structure
12
+
13
+ Generate Svelte components (`.svelte` files):
14
+
15
+ ### `BlueDitherTheme.svelte`
16
+ Root component:
17
+ ```svelte
18
+ <script>
19
+ import BlueDitherHeader from './BlueDitherHeader.svelte';
20
+ import BlueDitherHero from './BlueDitherHero.svelte';
21
+ </script>
22
+
23
+ <div class="bd-root">
24
+ <BlueDitherHero />
25
+ <BlueDitherHeader />
26
+ </div>
27
+
28
+ <style src="./bluedither.css"></style>
29
+ ```
30
+
31
+ ### `BlueDitherHeader.svelte`
32
+ Navbar with content tokens inlined:
33
+ ```svelte
34
+ <script>
35
+ const navItems = ['HOME', 'FEATURES', 'FAQ', 'CONTACT'];
36
+ </script>
37
+
38
+ <header class="bd-header">
39
+ <div class="bd-header-inner">
40
+ <div class="bd-logo">COMPANYLOGO</div>
41
+ <nav class="bd-nav">
42
+ {#each navItems as item}
43
+ <a class="bd-nav-item" href="#">{item}</a>
44
+ {/each}
45
+ </nav>
46
+ <a class="bd-cta" href="#">
47
+ <span class="bd-cta-text">SIGN UP</span>
48
+ </a>
49
+ </div>
50
+ </header>
51
+ ```
52
+
53
+ ### `BlueDitherHero.svelte`
54
+ Hero section with shader initialization using `onMount`:
55
+ ```svelte
56
+ <script>
57
+ import { onMount, onDestroy } from 'svelte';
58
+
59
+ let shaderParent;
60
+ let shaderMount = null;
61
+
62
+ onMount(async () => {
63
+ const { ShaderMount, ditheringFragmentShader, getShaderColorFromString } = await import('./paper-shaders-bundle.js');
64
+ shaderMount = new ShaderMount(shaderParent, ditheringFragmentShader, uniforms, opts, speed, 0, 2);
65
+ });
66
+
67
+ onDestroy(() => {
68
+ shaderMount?.dispose();
69
+ });
70
+ </script>
71
+
72
+ <div class="bd-hero">
73
+ <div class="bd-hero-inner">
74
+ <div bind:this={shaderParent} class="bd-shader-layer"></div>
75
+ <div class="bd-hero-content">
76
+ <div class="bd-headline">AN ABSOLUTELY<br>FANTASTIC HEADLINE.</div>
77
+ <div class="bd-subheadline">Sub-headline text here.</div>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ ```
82
+
83
+ ## Svelte 5 Runes
84
+
85
+ If Svelte 5 is detected (check `svelte` version in package.json):
86
+ - Use `$state()` instead of `let` for reactive variables
87
+ - Use `$effect()` instead of `onMount` for side effects with cleanup
88
+
89
+ ## SvelteKit
90
+
91
+ If SvelteKit is detected:
92
+ - Place components in `src/lib/` directory
93
+ - Import shader bundle using `$lib/` alias
94
+
95
+ ## File Output
96
+
97
+ | File | Contents |
98
+ |------|----------|
99
+ | `BlueDitherTheme.svelte` | Root layout component |
100
+ | `BlueDitherHeader.svelte` | Navbar component |
101
+ | `BlueDitherHero.svelte` | Hero + shader component |
102
+ | `bluedither.css` | CSS custom properties + rules |
103
+ | `paper-shaders-bundle.js` | Shader library (copied) |
@@ -0,0 +1,54 @@
1
+ # Vanilla HTML Generation
2
+
3
+ When the target project is **plain HTML/CSS/JS** (no framework detected), follow these patterns.
4
+
5
+ ## Output Structure
6
+
7
+ Generate a single `index.html` file containing:
8
+ 1. Inline `<style>` with CSS custom properties from tokens + all `.bd-*` class rules
9
+ 2. Semantic HTML matching `structure.json` exactly
10
+ 3. `<script type="module">` at the end of `<body>` that:
11
+ - Imports `bluedither-shader.js` (or the bundled version)
12
+ - Initializes `ShaderMount` on `#bd-shader-parent`
13
+
14
+ ## CSS Custom Properties
15
+
16
+ Compute `clamp()` values from all `referencePx` and spacing tokens using:
17
+ ```
18
+ clamp(max*0.55 rem, px/designWidth*100 vw, px/16 rem)
19
+ ```
20
+
21
+ Emit all properties under `:root` using the `--bd-*` namespace. See `template/index.html` for the exact variable names.
22
+
23
+ ## Google Fonts
24
+
25
+ Include `<link>` tags for `typography.primaryFont` and `typography.secondaryFont` via Google Fonts CDN:
26
+ ```html
27
+ <link href="https://fonts.googleapis.com/css2?family=FONT_NAME:wght@400;700&display=swap" rel="stylesheet">
28
+ ```
29
+ URL-encode the font family name.
30
+
31
+ ## Shader Module
32
+
33
+ Copy `theme/shaders/bluedither-shader.js` and `theme/shaders/paper-shaders-bundle.js` to the target project.
34
+
35
+ Import and initialize inline:
36
+ ```js
37
+ import { ShaderMount, ditheringFragmentShader, getShaderColorFromString } from './paper-shaders-bundle.js';
38
+ ```
39
+
40
+ Map shader tokens to uniforms as shown in `template/index.html`'s existing `<script type="module">` block.
41
+
42
+ ## File Output
43
+
44
+ | File | Contents |
45
+ |------|----------|
46
+ | `index.html` | Complete rendered page |
47
+ | `bluedither-shader.js` | Shader module (copied) |
48
+ | `paper-shaders-bundle.js` | Shader library bundle (copied) |
49
+
50
+ ## Content Handling
51
+
52
+ - Replace `{{content.*}}` placeholders with token values
53
+ - `{{content.headline}}` newlines become `<br>` tags
54
+ - `{{#each content.navItems}}` expands to repeated `<a>` elements
@@ -0,0 +1,104 @@
1
+ # Vue Generation
2
+
3
+ When the target project uses **Vue** (detected via `vue` in package.json dependencies), follow these patterns.
4
+
5
+ ## Detection
6
+
7
+ Vue is detected when `package.json` contains `vue` in `dependencies` or `devDependencies`. Also check for:
8
+ - `nuxt` (Nuxt.js)
9
+ - TypeScript: present if `typescript` is in devDependencies or `tsconfig.json` exists
10
+
11
+ ## Component Structure
12
+
13
+ Generate Vue Single File Components (SFCs) using `<script setup>` syntax (Vue 3):
14
+
15
+ ### `BlueDitherTheme.vue`
16
+ Root component:
17
+ ```vue
18
+ <script setup>
19
+ import BlueDitherHeader from './BlueDitherHeader.vue';
20
+ import BlueDitherHero from './BlueDitherHero.vue';
21
+ </script>
22
+
23
+ <template>
24
+ <div class="bd-root">
25
+ <BlueDitherHero />
26
+ <BlueDitherHeader />
27
+ </div>
28
+ </template>
29
+
30
+ <style src="./bluedither.css"></style>
31
+ ```
32
+
33
+ ### `BlueDitherHeader.vue`
34
+ Navbar with logo, nav items, CTA:
35
+ ```vue
36
+ <script setup>
37
+ const navItems = ['HOME', 'FEATURES', 'FAQ', 'CONTACT'];
38
+ const ctaText = 'SIGN UP';
39
+ const companyName = 'COMPANYLOGO';
40
+ </script>
41
+
42
+ <template>
43
+ <header class="bd-header">
44
+ <div class="bd-header-inner">
45
+ <div class="bd-logo">{{ companyName }}</div>
46
+ <nav class="bd-nav">
47
+ <a v-for="item in navItems" :key="item" class="bd-nav-item" href="#">{{ item }}</a>
48
+ </nav>
49
+ <a class="bd-cta" href="#">
50
+ <span class="bd-cta-text">{{ ctaText }}</span>
51
+ </a>
52
+ </div>
53
+ </header>
54
+ </template>
55
+ ```
56
+
57
+ ### `BlueDitherHero.vue`
58
+ Hero section with shader initialization using `onMounted`:
59
+ ```vue
60
+ <script setup>
61
+ import { ref, onMounted, onUnmounted } from 'vue';
62
+
63
+ const shaderParent = ref(null);
64
+ let shaderMount = null;
65
+
66
+ onMounted(async () => {
67
+ const { ShaderMount, ditheringFragmentShader, getShaderColorFromString } = await import('./paper-shaders-bundle.js');
68
+ shaderMount = new ShaderMount(shaderParent.value, ditheringFragmentShader, uniforms, opts, speed, 0, 2);
69
+ });
70
+
71
+ onUnmounted(() => {
72
+ shaderMount?.dispose();
73
+ });
74
+ </script>
75
+
76
+ <template>
77
+ <div class="bd-hero">
78
+ <div class="bd-hero-inner">
79
+ <div ref="shaderParent" class="bd-shader-layer"></div>
80
+ <div class="bd-hero-content">
81
+ <div class="bd-headline">...</div>
82
+ <div class="bd-subheadline">...</div>
83
+ </div>
84
+ </div>
85
+ </div>
86
+ </template>
87
+ ```
88
+
89
+ ## TypeScript
90
+
91
+ If TypeScript is detected, add `lang="ts"` to `<script setup lang="ts">`. Type the template ref:
92
+ ```ts
93
+ const shaderParent = ref<HTMLDivElement | null>(null);
94
+ ```
95
+
96
+ ## File Output
97
+
98
+ | File | Contents |
99
+ |------|----------|
100
+ | `BlueDitherTheme.vue` | Root layout SFC |
101
+ | `BlueDitherHeader.vue` | Navbar SFC |
102
+ | `BlueDitherHero.vue` | Hero + shader SFC |
103
+ | `bluedither.css` | CSS custom properties + rules |
104
+ | `paper-shaders-bundle.js` | Shader library (copied) |
package/theme/rules.md ADDED
@@ -0,0 +1,71 @@
1
+ # BlueDither Design Language Rules
2
+
3
+ These rules govern how tokens should be applied when an AI agent generates or extends BlueDither-themed components. They encode the design intent behind the token values.
4
+
5
+ ## Color Relationships
6
+
7
+ - **Background** (`colors.background`) is always the darkest value — near-black with a color tint (e.g., deep navy `#040037`).
8
+ - **Primary** (`colors.primary`) is a saturated accent used for the navbar background and shader foreground. It should be a bold, high-chroma color.
9
+ - **Text** (`colors.text`) is always light (white or near-white) for contrast against the dark background.
10
+ - **CTA** uses inverted contrast: light background (`colors.ctaBackground`) with dark text (`colors.ctaText`). The CTA must visually pop against both the navbar and the hero.
11
+ - **Shader colors**: `shaderFront` typically matches `colors.primary`. `shaderBack` is usually transparent (`#00000000`) so the page background shows through the dither pattern.
12
+ - When adjusting colors, maintain a minimum contrast ratio of 4.5:1 for body text and 3:1 for large text (headline).
13
+
14
+ ## Type Scale
15
+
16
+ - **Two font families only**: a display/primary font (bold, impactful — e.g., Bebas Neue) and a secondary/mono font (readable, technical — e.g., Space Mono).
17
+ - **Primary font** is used for: logo, nav items, headline, CTA text. Always set to `primaryFontWeight` (typically 700).
18
+ - **Secondary font** is used for: sub-headline only. Always set to `secondaryFontWeight` (typically 400).
19
+ - **Size hierarchy** (from largest to smallest): headline → logo ≈ nav ≈ CTA → sub-headline. The headline should be dramatically larger (5-7x) than the sub-headline.
20
+ - All typography `referencePx` values are relative to `layout.designWidth`. At render time they become `clamp()` values for fluid responsiveness.
21
+ - The formula: `clamp(max*0.55 rem, px/designWidth*100 vw, px/16 rem)` — scales down to 55% on small screens.
22
+
23
+ ## Spacing System
24
+
25
+ - All spacing values are in reference px at `designWidth`, converted to `clamp()` the same way as typography.
26
+ - **Header padding** is tight — just enough to frame the logo/nav/CTA row.
27
+ - **Hero padding** is generous at the bottom and sides, minimal at the top — content anchors to the bottom-left.
28
+ - **Nav gap** should be wide enough for clear separation but not so wide the items lose visual grouping.
29
+ - **CTA padding** is compact (button should feel tight/punchy, not bloated).
30
+ - **Border radius** is intentionally small (4px default). The design language is angular, not rounded.
31
+
32
+ ## Font Usage
33
+
34
+ - All text in the navbar uses the primary font.
35
+ - Only the sub-headline uses the secondary font.
36
+ - Text transform (`uppercase`) is applied via CSS, not by modifying content strings. The `textTransform` token on sub-headline controls this.
37
+ - When generating content, write it in the natural case for the slot — CSS handles visual casing.
38
+
39
+ ## Opacity Conventions
40
+
41
+ - `opacity.navLinks` dims the navigation row slightly (default 0.87) to create depth hierarchy. The logo and CTA remain at full opacity.
42
+ - Opacity should stay above 0.7 for accessibility. Values below 0.5 make text unreadable.
43
+
44
+ ## Shader Integration
45
+
46
+ - The shader layer is always **full-bleed** behind hero content, positioned absolute.
47
+ - Shader `rotation` rotates the entire shader layer via CSS `rotate()`.
48
+ - The shader is a WebGL `<canvas>` managed by `ShaderMount` from `@paper-design/shaders`.
49
+ - Available shapes: simplex, warp, dots, wave, ripple, swirl, sphere. Default is `warp`.
50
+ - Available dither types: random, 2x2, 4x4, 8x8. Default is `2x2`.
51
+ - `speed` controls animation rate (0 = frozen, 0.18 = subtle motion, >1 = energetic).
52
+ - `scale` controls the zoom of the noise pattern. `size` controls dither pixel size.
53
+
54
+ ## Layout Constraints
55
+
56
+ - The layout is **exactly two visual sections**: a header (navbar) and a hero. Nothing else.
57
+ - The header sits on top with `z-index: 10`, the hero is positioned absolute behind it.
58
+ - Hero content aligns to the **bottom-left** (`justify-content: end; align-items: start`).
59
+ - The root container is `min-height: 100vh` with `overflow: clip`.
60
+ - The design is **not scrollable** — it's a single-viewport statement piece.
61
+
62
+ ## Extending the Theme
63
+
64
+ When the AI generates new components outside the locked template:
65
+ 1. Use only CSS custom properties from the `--bd-*` namespace.
66
+ 2. Follow the same two-font-family rule — never introduce a third font.
67
+ 3. Maintain the dark-background + light-text paradigm.
68
+ 4. Use `colors.primary` as the accent for interactive elements.
69
+ 5. Use `colors.ctaBackground`/`colors.ctaText` for any additional buttons.
70
+ 6. New spacing should derive from existing tokens (multiples or fractions of `heroPaddingX`).
71
+ 7. Keep the angular, minimal aesthetic — no rounded corners > 8px, no shadows, no gradients (the shader IS the visual interest).
@@ -0,0 +1,92 @@
1
+ /**
2
+ * BlueDither Shader Module
3
+ *
4
+ * Uses the actual @paper-design/shaders library (ShaderMount + ditheringFragmentShader)
5
+ * for pixel-accurate rendering matching the Paper design tool.
6
+ */
7
+
8
+ import {
9
+ ShaderMount,
10
+ ditheringFragmentShader,
11
+ DitheringShapes,
12
+ DitheringTypes,
13
+ getShaderColorFromString
14
+ } from './paper-shaders-bundle.js';
15
+
16
+ /**
17
+ * Mount the dithering shader into a parent element.
18
+ * ShaderMount creates its own canvas inside the parent.
19
+ *
20
+ * @param {HTMLElement} parentEl - Container element (shader fills it)
21
+ * @param {object} params - Token-driven params
22
+ * @returns {{ updateParams, destroy }}
23
+ */
24
+ export function createDitheringRenderer(parentEl, params) {
25
+ const shapeValue = DitheringShapes[params.shape] ?? DitheringShapes.warp;
26
+ const typeValue = DitheringTypes[params.type] ?? DitheringTypes['2x2'];
27
+
28
+ const uniforms = {
29
+ u_colorFront: getShaderColorFromString(params.colorFront),
30
+ u_colorBack: getShaderColorFromString(params.colorBack),
31
+ u_shape: shapeValue,
32
+ u_type: typeValue,
33
+ u_pxSize: params.size,
34
+ u_scale: params.scale,
35
+ u_rotation: parseFloat(params.rotation) || 180,
36
+ u_originX: 0.5,
37
+ u_originY: 0.5,
38
+ u_worldWidth: 0,
39
+ u_worldHeight: 0,
40
+ u_fit: 0,
41
+ u_offsetX: 0,
42
+ u_offsetY: 0,
43
+ };
44
+
45
+ const mount = new ShaderMount(
46
+ parentEl,
47
+ ditheringFragmentShader,
48
+ uniforms,
49
+ { alpha: true, premultipliedAlpha: false },
50
+ params.speed, // speed
51
+ 0, // frame
52
+ 2, // minPixelRatio
53
+ );
54
+
55
+ return {
56
+ updateParams(newParams) {
57
+ const updatedUniforms = {};
58
+
59
+ if (newParams.colorFront !== undefined) {
60
+ updatedUniforms.u_colorFront = getShaderColorFromString(newParams.colorFront);
61
+ }
62
+ if (newParams.colorBack !== undefined) {
63
+ updatedUniforms.u_colorBack = getShaderColorFromString(newParams.colorBack);
64
+ }
65
+ if (newParams.shape !== undefined) {
66
+ updatedUniforms.u_shape = DitheringShapes[newParams.shape] ?? shapeValue;
67
+ }
68
+ if (newParams.type !== undefined) {
69
+ updatedUniforms.u_type = DitheringTypes[newParams.type] ?? typeValue;
70
+ }
71
+ if (newParams.size !== undefined) {
72
+ updatedUniforms.u_pxSize = newParams.size;
73
+ }
74
+ if (newParams.scale !== undefined) {
75
+ updatedUniforms.u_scale = newParams.scale;
76
+ }
77
+ if (newParams.rotation !== undefined) {
78
+ updatedUniforms.u_rotation = parseFloat(newParams.rotation) || 180;
79
+ }
80
+ if (newParams.speed !== undefined) {
81
+ mount.setSpeed(newParams.speed);
82
+ }
83
+
84
+ if (Object.keys(updatedUniforms).length > 0) {
85
+ mount.setUniforms(updatedUniforms);
86
+ }
87
+ },
88
+ destroy() {
89
+ mount.dispose();
90
+ }
91
+ };
92
+ }