BlueSky & more on desktop lazurite.stormlightlabs.org/
tauri rust typescript bluesky appview atproto solid
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

build: design spec + tailwind

+111 -118
+77
docs/design.md
··· 1 + # Design System Spec 2 + 3 + ## Overview 4 + 5 + This interface is a high-contrast, grid-based environment built on pure black (`#000000`) with a single electric accent. The visual system avoids template-like framing by replacing hard structure (lines, dividers, borders) with tonal layering and intentional asymmetry. 6 + 7 + Design goal: focused, cinematic, and modern. 8 + 9 + ## Color and Surfaces 10 + 11 + Space is defined by light and depth, not lines. 12 + 13 + - **No-Line Rule:** Do not use 1px solid borders for sectioning. Separate regions by stepping between surface tokens (for example, `surface` to `surface_container_low`). 14 + - **Surface hierarchy:** 15 + - **Base layer:** `surface_container_lowest` (`#000000`) for deepest background and the void behind the app rail. 16 + - **Primary work surface:** `surface` (`#0e0e0e`) for the default content canvas. 17 + - **Elevated containers:** `surface_container` (`#191919`) and `surface_container_high` (`#1f1f1f`) for cards and interactive panels. 18 + - **Glass overlays:** Floating modals and high-priority overlays use `surface_container_highest` at 70% opacity with `backdrop-blur: 20px` for an obsidian-glass effect. 19 + - **Primary CTA texture:** Use a linear gradient (135deg) from `primary` (`#7dafff`) to `primary_dim` (`#0073de`), not a flat fill. 20 + 21 + ## Typography 22 + 23 + Use **Google Sans** as both readability and structure. 24 + 25 + - **Display and headline scale:** `display-lg` (3.5rem) and `headline-lg` (2rem) with tight tracking (`-0.02em`). 26 + - **Body and labels:** `body-md` (0.875rem) is the default body size. Use `on_surface_variant` (`#ababab`) for secondary text against `on_surface` titles. 27 + - **Asymmetric layout:** Avoid centered hero alignment. Keep headlines left-aligned and place supporting metadata in `label-sm` on the far right, using `spacing-8` (2rem) to preserve separation. 28 + 29 + ## Elevation and Depth 30 + 31 + In a black UI, conventional drop shadows are low impact. Depth is created through tone and glow. 32 + 33 + - **Layering principle:** Lift components by moving up the surface token scale (for example, `surface_container_low` on top of `surface`). 34 + - **Ambient glow:** For floating UI (tooltips, dropdowns), use a `primary` shadow at 5% opacity with a 40px blur. 35 + - **Ghost border fallback:** If a boundary is required for accessibility, use `outline_variant` (`#484848`) at 20% opacity. 36 + 37 + ## Component Rules 38 + 39 + ### App Rail 40 + 41 + - Fixed-width rail on the far left using `surface_container_lowest`. 42 + - Icons use 1.5pt strokes. 43 + - Active icon color: `primary` (`#7dafff`); inactive: `on_surface_variant`. 44 + - No persistent labels; labels may appear on hover. 45 + 46 + ### Buttons 47 + 48 + - **Primary:** Gradient fill (`primary` to `primary_dim`), text color `on_primary_fixed` (black), radius `full`. 49 + - **Secondary:** Fill `surface_container_highest`, text `on_surface`, no border, radius `md` (0.75rem). 50 + - **Tertiary:** Transparent background, text `primary`; use for low-emphasis actions (for example, Cancel). 51 + 52 + ### Cards and Lists 53 + 54 + - Do not use horizontal dividers between list items. 55 + - Separate items with `spacing-2` (0.5rem) vertical space. 56 + - Use subtle hover state with `surface_bright` at 5% opacity. 57 + - Card radius must be `lg` (1rem) or `xl` (1.5rem). 58 + 59 + ### Input Fields 60 + 61 + - Use minimalist containers with `surface_container_low`. 62 + - Focus state: 1px ghost border using `primary` at 50% opacity plus subtle glow. 63 + - Avoid thick, fully opaque outlines. 64 + 65 + ## Dos & Don'ts 66 + 67 + ### Do 68 + 69 + - Preserve large areas of pure `#000000` to maintain visual breathing room. 70 + - Use `secondary_container` for chips/tags to create soft contrast. 71 + - Apply `full` roundedness to key interactive controls (for example, buttons and search bars) to balance the hard screen geometry. 72 + 73 + ### Do Not 74 + 75 + - Use pure white (`#FFFFFF`) for long-form body copy; use `on_secondary_container` (`#c9d1dd`) to reduce eye strain. 76 + - Use fully opaque borders. 77 + - Stack more than three surface-container depth levels; use a backdrop-blur overlay instead.
+3 -116
src/App.css
··· 1 - .logo.vite:hover { 2 - filter: drop-shadow(0 0 2em #747bff); 3 - } 4 - 5 - .logo.solid:hover { 6 - filter: drop-shadow(0 0 2em #2f5d90); 7 - } 8 - :root { 9 - font-family: Inter, Avenir, Helvetica, Arial, sans-serif; 10 - font-size: 16px; 11 - line-height: 24px; 12 - font-weight: 400; 13 - 14 - color: #0f0f0f; 15 - background-color: #f6f6f6; 16 - 17 - font-synthesis: none; 18 - text-rendering: optimizeLegibility; 19 - -webkit-font-smoothing: antialiased; 20 - -moz-osx-font-smoothing: grayscale; 21 - -webkit-text-size-adjust: 100%; 22 - } 23 - 24 - .container { 25 - margin: 0; 26 - padding-top: 10vh; 27 - display: flex; 28 - flex-direction: column; 29 - justify-content: center; 30 - text-align: center; 31 - } 32 - 33 - .logo { 34 - height: 6em; 35 - padding: 1.5em; 36 - will-change: filter; 37 - transition: 0.75s; 38 - } 39 - 40 - .logo.tauri:hover { 41 - filter: drop-shadow(0 0 2em #24c8db); 42 - } 43 - 44 - .row { 45 - display: flex; 46 - justify-content: center; 47 - } 48 - 49 - a { 50 - font-weight: 500; 51 - color: #646cff; 52 - text-decoration: inherit; 53 - } 54 - 55 - a:hover { 56 - color: #535bf2; 57 - } 58 - 59 - h1 { 60 - text-align: center; 61 - } 62 - 63 - input, 64 - button { 65 - border-radius: 8px; 66 - border: 1px solid transparent; 67 - padding: 0.6em 1.2em; 68 - font-size: 1em; 69 - font-weight: 500; 70 - font-family: inherit; 71 - color: #0f0f0f; 72 - background-color: #ffffff; 73 - transition: border-color 0.25s; 74 - box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); 75 - } 76 - 77 - button { 78 - cursor: pointer; 79 - } 80 - 81 - button:hover { 82 - border-color: #396cd8; 83 - } 84 - button:active { 85 - border-color: #396cd8; 86 - background-color: #e8e8e8; 87 - } 88 - 89 - input, 90 - button { 91 - outline: none; 92 - } 93 - 94 - #greet-input { 95 - margin-right: 5px; 96 - } 97 - 98 - @media (prefers-color-scheme: dark) { 99 - :root { 100 - color: #f6f6f6; 101 - background-color: #2f2f2f; 102 - } 103 - 104 - a:hover { 105 - color: #24c8db; 106 - } 107 - 108 - input, 109 - button { 110 - color: #ffffff; 111 - background-color: #0f0f0f98; 112 - } 113 - button:active { 114 - background-color: #0f0f0f69; 115 - } 116 - } 1 + @import "tailwindcss"; 2 + @plugin "@egoist/tailwindcss-icons"; 3 + @plugin "@tailwindcss/forms";
+16
src/test/setup.ts
··· 1 + /* eslint-disable unicorn/consistent-function-scoping */ 2 + import { cleanup } from "@solidjs/testing-library"; 3 + import "@testing-library/jest-dom/vitest"; 4 + import { afterEach, vi } from "vitest"; 5 + 6 + vi.mock( 7 + "solid-motionone", 8 + () => ({ 9 + Motion: new Proxy({}, { get: () => (props: { children?: unknown }) => props.children as unknown }), 10 + Presence: (props: { children?: unknown }) => props.children as unknown, 11 + }), 12 + ); 13 + 14 + Object.defineProperty(globalThis, "scrollTo", { value: vi.fn(), writable: true }); 15 + 16 + afterEach(cleanup);
+15 -2
vite.config.ts
··· 1 + import tailwindcss from "@tailwindcss/vite"; 2 + import { fileURLToPath } from "node:url"; 1 3 import { defineConfig } from "vite"; 2 4 import solid from "vite-plugin-solid"; 5 + import type { ViteUserConfig } from "vitest/config"; 6 + 7 + const test: ViteUserConfig["test"] = { 8 + environment: "jsdom", 9 + globals: true, 10 + setupFiles: ["src/test/setup.ts"], 11 + server: { deps: { inline: ["@solidjs/router"] } }, 12 + ui: false, 13 + watch: false, 14 + }; 3 15 4 16 const host = process.env.TAURI_DEV_HOST; 5 17 6 - // https://vite.dev/config/ 7 18 export default defineConfig(async () => ({ 8 - plugins: [solid()], 19 + plugins: [solid(), tailwindcss()], 9 20 clearScreen: false, 10 21 server: { 11 22 port: 1420, ··· 14 25 hmr: host ? { protocol: "ws", host, port: 1421 } : undefined, 15 26 watch: { ignored: ["**/src-tauri/**"] }, 16 27 }, 28 + resolve: { alias: { "$": fileURLToPath(new URL("src", import.meta.url)) } }, 29 + test, 17 30 }));