diff --git a/astro.config.mjs b/astro.config.mjs index 13aba3a..7a468f0 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -167,6 +167,8 @@ export default defineConfig({ ], components: { PageTitle: './src/components/overrides/PageTitle.astro', + Sidebar: './src/components/overrides/Sidebar.astro', + SiteTitle: './src/components/overrides/SiteTitle.astro', Head: './src/components/overrides/Head.astro', }, expressiveCode: { @@ -201,11 +203,11 @@ export default defineConfig({ { label: 'Guides', items: [ - { label: 'Overview', slug: 'chatbot/guides' }, { label: 'Death Counter', slug: 'chatbot/guides/death-counter' }, { label: 'Lurk Command', slug: 'chatbot/guides/lurk-command' }, { label: 'Countdown Command', slug: 'chatbot/guides/countdown-command' }, { label: 'Viewer Input', slug: 'chatbot/guides/viewer-input' }, + { label: 'All guides', slug: 'chatbot/guides' }, ], }, { @@ -230,16 +232,34 @@ export default defineConfig({ ], }, { - label: 'Overlays', + label: 'SE.Live', + link: '/selive', + icon: 'window', + items: [ + { label: 'Overview', slug: 'selive' }, + { label: 'Getting started', items: [ + { label: 'Install and connect', slug: 'selive/getting-started' }, + ] }, + { label: 'Guides', items: [ + { label: 'Add overlays and alerts', slug: 'selive/guides/overlays-and-alerts' }, + { label: 'All guides', slug: 'selive/guides' }, + ] }, + { label: 'Troubleshooting', slug: 'selive/troubleshooting' }, + ], + }, + { + label: 'Overlays & Alerts', link: '/overlays', icon: 'desktop', items: [ { label: 'Overview', slug: 'overlays' }, { label: 'Getting Started', slug: 'overlays/getting-started' }, { - label: 'Your First Custom Widget', - slug: 'overlays/first-custom-widget', - badge: { text: 'Tutorial', variant: 'tip' }, + label: 'Guides', + items: [ + { label: 'Build your first custom widget', slug: 'overlays/first-custom-widget' }, + { label: 'Custom code in AlertBoxes', slug: 'overlays/custom-code-in-alertbox' }, + ], }, { label: 'Custom Widgets', @@ -250,23 +270,26 @@ export default defineConfig({ { label: 'Session Data Reference', slug: 'overlays/session-data' }, ], }, - { label: 'Custom Code in AlertBoxes', slug: 'overlays/custom-code-in-alertbox' }, { label: 'Editor Shortcuts', slug: 'overlays/overlay-editor-shortcuts' }, ], }, { - label: 'WebSockets', - link: '/websockets', + label: 'Developers', + link: '/developers', icon: 'server', items: [ - { label: 'Introduction', slug: 'websockets' }, + { label: 'Overview', slug: 'developers' }, + { label: 'API Reference', link: 'https://dev.streamelements.com/' }, + { label: 'Build custom widgets', link: '/overlays/first-custom-widget' }, + { label: 'WebSockets', slug: 'websockets' }, { label: 'Client Examples', slug: 'websockets/examples' }, { - label: 'Topics', + label: 'WebSocket topics', items: [ { label: 'Overview', slug: 'websockets/topics' }, { label: 'Session & Stream', + collapsed: true, items: [ 'websockets/topics/channel-session-update', 'websockets/topics/channel-session-reset', @@ -275,6 +298,7 @@ export default defineConfig({ }, { label: 'Activity & Engagement', + collapsed: true, items: [ 'websockets/topics/channel-activities', 'websockets/topics/channel-chat-message', @@ -287,6 +311,7 @@ export default defineConfig({ }, { label: 'Overlays', + collapsed: true, items: [ 'websockets/topics/channel-overlay-action', 'websockets/topics/channel-overlay-broadcast', @@ -295,6 +320,7 @@ export default defineConfig({ }, { label: 'Chatbot', + collapsed: true, items: [ 'websockets/topics/chatbot-status', 'websockets/topics/chatbot-audiencequeue', @@ -308,17 +334,6 @@ export default defineConfig({ }, ], }, - { - label: 'API Reference', - link: 'https://dev.streamelements.com/', - icon: 'external', - attrs: { target: '_blank', rel: 'noopener' }, - }, - { - label: 'Changelog', - link: '/changelog', - icon: 'rocket', - }, ], { exclude: ['/', '/404'], diff --git a/public/brand/streamelements-dark.svg b/public/brand/streamelements-dark.svg new file mode 100644 index 0000000..242800e --- /dev/null +++ b/public/brand/streamelements-dark.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/brand/streamelements-light.svg b/public/brand/streamelements-light.svg new file mode 100644 index 0000000..9bfea1a --- /dev/null +++ b/public/brand/streamelements-light.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/ChatExample.astro b/src/components/ChatExample.astro index 0fb952c..94087e8 100644 --- a/src/components/ChatExample.astro +++ b/src/components/ChatExample.astro @@ -97,7 +97,7 @@ const resolved = messages.map((msg) => { )} {!msg.isSystem && ( - + {msg.username}: )} @@ -174,8 +174,16 @@ const resolved = messages.map((msg) => { border-radius: 2px; } .chat-username { + color: var(--chat-user-color); font-weight: 700; - white-space: nowrap; + min-width: 0; + overflow-wrap: anywhere; + } + :global([data-theme='light']) .chat-username { + color: color-mix(in srgb, var(--chat-user-color) 60%, black); + } + :global([data-theme='dark']) .chat-username { + color: color-mix(in srgb, var(--chat-user-color) 75%, white); } .chat-text { min-width: 0; diff --git a/src/components/changelog/ChangelogList.astro b/src/components/changelog/ChangelogList.astro index 1012f57..806a49c 100644 --- a/src/components/changelog/ChangelogList.astro +++ b/src/components/changelog/ChangelogList.astro @@ -27,7 +27,7 @@ const rendered = await Promise.all(

- {entry.data.title} + {entry.data.title.split(/(?<=[._/])/).map((part, index) => <>{index > 0 && }{part})}

diff --git a/src/components/overrides/PageTitle.astro b/src/components/overrides/PageTitle.astro index 360afa0..e970c08 100644 --- a/src/components/overrides/PageTitle.astro +++ b/src/components/overrides/PageTitle.astro @@ -14,10 +14,16 @@ const { platforms, wsTopic, scope, status, syntax, arguments: args, aliases, acc Astro.locals.starlightRoute.entry.data; --- - +
{platforms && platforms.length > 0 && } {wsTopic && } {syntax && } {(access || requires || cooldown || (aliases && aliases.length > 0)) && ( )} + + diff --git a/src/components/overrides/Sidebar.astro b/src/components/overrides/Sidebar.astro new file mode 100644 index 0000000..e950c36 --- /dev/null +++ b/src/components/overrides/Sidebar.astro @@ -0,0 +1,116 @@ +--- +import { Icon } from '@astrojs/starlight/components'; +import SidebarSublist from '@astrojs/starlight/components/SidebarSublist.astro'; +import SidebarPersister from '@astrojs/starlight/components/SidebarPersister.astro'; +import MobileMenuFooter from 'virtual:starlight/components/MobileMenuFooter'; + +const { sidebar } = Astro.locals.starlightRoute; +const { topics, isPageWithTopic } = Astro.locals.starlightSidebarTopics; +const current = topics.find((topic) => topic.isCurrent); +--- + +
+ {isPageWithTopic && ( + +
+ + + Documentation{current?.label ?? 'Choose product'} + + + +
+
+ )} +
+ + + +
+ +
+
+ + + + diff --git a/src/components/overrides/SiteTitle.astro b/src/components/overrides/SiteTitle.astro new file mode 100644 index 0000000..d06ba59 --- /dev/null +++ b/src/components/overrides/SiteTitle.astro @@ -0,0 +1,22 @@ +--- +const { siteTitleHref } = Astro.locals.starlightRoute; +--- + + + + + Docs + + + diff --git a/src/content/docs/developers.mdx b/src/content/docs/developers.mdx new file mode 100644 index 0000000..1125a0f --- /dev/null +++ b/src/content/docs/developers.mdx @@ -0,0 +1,15 @@ +--- +title: Developers +description: Build StreamElements integrations with the HTTP API, WebSockets, and custom widgets. +--- + +import { CardGrid, LinkCard } from '@astrojs/starlight/components'; + +Choose the tools you need to build on StreamElements. + + + + + + + diff --git a/src/content/docs/index.mdx b/src/content/docs/index.mdx index b33d13d..ba739a9 100644 --- a/src/content/docs/index.mdx +++ b/src/content/docs/index.mdx @@ -1,6 +1,6 @@ --- title: StreamElements Documentation -description: The official documentation for StreamElements — chatbot, overlays, realtime websockets, and the developer API. +description: The official documentation for StreamElements — Chatbot, SE.Live, overlays, and developer tools. # The default would double the brand ("StreamElements Documentation | StreamElements Docs"). head: - tag: title @@ -35,19 +35,19 @@ import { CardGrid, LinkCard } from '@astrojs/starlight/components'; description="Commands, variables, timers, spam filters, and interactive modules for your chat." /> <LinkCard - title="Overlays" - href="/overlays" - description="Build stream overlays — from your first AlertBox to fully custom-coded widgets." + title="SE.Live" + href="/selive" + description="Set up SE.Live in OBS Studio and find guides for your stream." /> <LinkCard - title="WebSockets" - href="/websockets" - description="Subscribe to realtime events — tips, activities, chat, and overlay control — over the Astro protocol." + title="Overlays & Alerts" + href="/overlays" + description="Build stream overlays — from your first AlertBox to fully custom-coded widgets." /> <LinkCard - title="API Reference" - href="https://dev.streamelements.com/" - description="The StreamElements HTTP API for building your own integrations." + title="Developers" + href="/developers" + description="Build integrations with WebSockets, the HTTP API, and custom widgets." /> <LinkCard title="Changelog" diff --git a/src/content/docs/selive/getting-started.mdx b/src/content/docs/selive/getting-started.mdx new file mode 100644 index 0000000..541cc08 --- /dev/null +++ b/src/content/docs/selive/getting-started.mdx @@ -0,0 +1,20 @@ +--- +title: Install and connect SE.Live +description: Download SE.Live, install it in OBS Studio, and connect your StreamElements account. +--- + +## Install + +Download the installer from the [SE.Live page](https://streamelements.com/selive). Check the current requirements there before installing. Close OBS, run the installer, then reopen OBS. + +## Connect + +Open **StreamElements → Login** in OBS and sign in. To connect your streaming service, open **Settings → Stream** and follow the account connection steps. + +Open chat and activity through **StreamElements → SE.Live Docks**. + +For the full setup walkthrough, see the [SE.Live support guide](https://support.streamelements.com/hc/en-us/articles/18771457842066). + +## Next step + +[Add overlays and alerts](/selive/guides/overlays-and-alerts) to your scene. diff --git a/src/content/docs/selive/guides/index.mdx b/src/content/docs/selive/guides/index.mdx new file mode 100644 index 0000000..c007b66 --- /dev/null +++ b/src/content/docs/selive/guides/index.mdx @@ -0,0 +1,18 @@ +--- +title: SE.Live guides +description: Guides for overlays, scenes, and streaming with SE.Live. +--- + +import { CardGrid, LinkCard } from '@astrojs/starlight/components'; + +## Overlays + +<CardGrid> + <LinkCard title="Add overlays and alerts" href="/selive/guides/overlays-and-alerts" description="Add an AlertBox to an overlay and bring it into OBS." /> +</CardGrid> + +## More from the Help Center + +The [SE.Live Help Center](https://support.streamelements.com/hc/en-us/sections/10474402361490-SE-Live) has detailed guides for scenes and sources, Canvas, and streaming formats. + +For setup, features, and multistreaming, open the [complete SE.Live guide](https://support.streamelements.com/hc/en-us/articles/18771457842066). diff --git a/src/content/docs/selive/guides/overlays-and-alerts.mdx b/src/content/docs/selive/guides/overlays-and-alerts.mdx new file mode 100644 index 0000000..b725b69 --- /dev/null +++ b/src/content/docs/selive/guides/overlays-and-alerts.mdx @@ -0,0 +1,22 @@ +--- +title: Add overlays and alerts +description: Create an overlay and display it in your OBS scene with SE.Live. +--- + +## Create your overlay + +1. Open [My Overlays](https://streamelements.com/dashboard/overlays) and choose **New overlay**. +2. Choose a resolution that matches your scene. +3. In the editor, select **+ → Alerts → AlertBox** to add alerts. Configure the alert types you need, then save. + +The [overlay setup guide](/overlays/getting-started) includes screenshots of these steps. + +## Add it to OBS + +Copy the overlay URL. In OBS, add a **Browser Source** to your scene and paste the URL into its properties. Match the source dimensions to your overlay. Place it above sources that would cover it. + +Keep the overlay URL private. See the [SE.Live support guide](https://support.streamelements.com/hc/en-us/articles/18771457842066) for more on overlay setup. + +## Customize further + +For an overlay with your own HTML, CSS, and JavaScript, follow [Build your first custom widget](/overlays/first-custom-widget). diff --git a/src/content/docs/selive/index.mdx b/src/content/docs/selive/index.mdx new file mode 100644 index 0000000..0ff9f9e --- /dev/null +++ b/src/content/docs/selive/index.mdx @@ -0,0 +1,15 @@ +--- +title: SE.Live +description: Set up SE.Live for OBS Studio, add overlays, and find guides for your stream. +--- + +import { CardGrid, LinkCard } from '@astrojs/starlight/components'; + +SE.Live brings StreamElements chat, activity, and streaming tools into OBS Studio. Start with setup, then choose a guide for the task you want to complete. + +<CardGrid> + <LinkCard title="Install and connect" href="/selive/getting-started" description="Install SE.Live and sign in to your account." /> + <LinkCard title="Add overlays and alerts" href="/selive/guides/overlays-and-alerts" description="Bring your StreamElements overlay into an OBS scene." /> + <LinkCard title="Browse guides" href="/selive/guides" description="Find help with scenes, streaming formats, and more." /> + <LinkCard title="Troubleshooting" href="/selive/troubleshooting" description="Find support when setup or streaming goes wrong." /> +</CardGrid> diff --git a/src/content/docs/selive/troubleshooting.mdx b/src/content/docs/selive/troubleshooting.mdx new file mode 100644 index 0000000..3fbafee --- /dev/null +++ b/src/content/docs/selive/troubleshooting.mdx @@ -0,0 +1,17 @@ +--- +title: SE.Live troubleshooting +description: Find troubleshooting steps and contact support for SE.Live. +--- + +For installation, login, and streaming issues, start with [SE.Live Troubleshooting in the Help Center](https://support.streamelements.com/hc/en-us/articles/10474479415442). + +## Get help with your setup + +When asking for help, include: + +- Your OBS and SE.Live versions. +- What you were doing when the problem started. +- The exact error message, if there is one. +- Whether the problem happens every time. + +Use the [Help Center](https://support.streamelements.com/) to contact support. Remove stream keys and private overlay URLs from screenshots before sharing them. diff --git a/src/styles/custom.css b/src/styles/custom.css index 4a8e239..dccd5c1 100644 --- a/src/styles/custom.css +++ b/src/styles/custom.css @@ -1,84 +1,106 @@ -/* StreamElements Starlight theme. - Color tokens mirror the @frontend/ui design system (frontend monorepo, - packages/ui/src/styles/globals.css, OKLCH). To resync with the app, - update the --se-* block — everything else derives from it. */ - -/* Typography: Inter Variable (loaded via customCss entry in astro.config.mjs). - @frontend/ui intentionally ships no font family (each app sets its own), - so Inter is a docs-local choice, not a divergence. */ +/* Color values copied from @frontend/ui src/styles/tokens.css. + Keep these theme blocks in sync with that file. Starlight uses data-theme + instead of .dark; no frontend checkout or Tailwind is required to build. */ :root { --sl-font: 'Inter Variable', ui-sans-serif, system-ui, sans-serif; - - /* Brand + semantic colors (source token noted per line). */ - --se-primary: oklch(0.6426 0.1869 265.73); /* --primary / $uiPrimaryMain #5684FD */ - --se-primary-dark: oklch(0.68 0.16 265.73); /* .dark --primary */ - --se-success: oklch(0.797 0.1683 156.11); /* --success / $uiSuccessMain #46DC8E */ - --se-attention: oklch(0.7272 0.177 54.06); /* --attention / $uiAttentionMain #F98215 */ - --se-destructive: oklch(0.6383 0.2236 17.81); /* --destructive / $uiErrorMain #F53656 */ - --se-purple: oklch(0.6506 0.2049 293.08); /* --chart-5 */ - - /* Neutral ramp (hue 250, shared across modes in the app). */ + --se-radius: 0.625rem; + --se-radius-sm: 0.375rem; + --se-purple: oklch(0.6506 0.2049 293.08); --se-neutral-0: oklch(1 0 250); - --se-neutral-50: oklch(0.9861 0 250); /* light --background #f7f7f7 */ + --se-neutral-50: oklch(0.9861 0 250); --se-neutral-100: oklch(0.9683 0.0017 250); - --se-neutral-200: oklch(0.9345 0.0032 250); /* light --border */ + --se-neutral-200: oklch(0.9345 0.0032 250); --se-neutral-300: oklch(0.8811 0.0043 250); - --se-neutral-400: oklch(0.7107 0.0055 250); /* dark --muted-foreground */ + --se-neutral-400: oklch(0.7107 0.0055 250); --se-neutral-500: oklch(0.5555 0.0057 250); --se-neutral-600: oklch(0.4419 0.0052 250); --se-neutral-700: oklch(0.3715 0.0046 250); --se-neutral-800: oklch(0.2686 0.0076 250); --se-neutral-900: oklch(0.2046 0.006 250); - --se-neutral-950: oklch(0.1684 0.005 250); /* dark --background #131315 */ - --se-card-dark: oklch(0.2146 0.006 250); /* .dark --card #232428 */ - --se-sidebar-dark: oklch(0.19 0.005 250); /* .dark --sidebar */ + --se-neutral-950: oklch(0.1684 0.005 250); +} - /* Radii (--radius: 0.625rem base). */ - --se-radius: 0.625rem; - --se-radius-sm: 0.375rem; +:root[data-theme='light'] { + --se-background: oklch(0.9861 0 250); + --se-foreground: oklch(0 0 0 / 87%); + --se-card: oklch(1 0 250); + --se-popover: oklch(1 0 250); + --se-primary: oklch(0.56 0.1869 265.73); + --se-primary-foreground: oklch(1 0 0); + --se-muted-foreground: oklch(0 0 0 / 60%); + --se-destructive: oklch(0.58 0.2 25); + --se-success: oklch(0.797 0.1683 156.11); + --se-attention: oklch(0.7272 0.177 54.06); + --se-border: var(--se-neutral-200); + --se-ring: oklch(0.56 0.1869 265.73); + --se-shadow-float: 0 4px 12px rgb(0 0 0 / 0.09), 0 1px 3px rgb(0 0 0 / 0.06); + --se-sidebar: oklch(1 0 0); + --se-sidebar-accent: oklch(0.9653 0.0019 250); + --se-sidebar-border: oklch(0 0 0 / 10%); } -/* Starlight ramps derived from the tokens above. - IMPORTANT: theme ramps MUST be scoped to [data-theme=...] — this stylesheet - is unlayered and beats Starlight's layered theme styles regardless of - specificity, so an unscoped :root ramp would poison the other mode. - Starlight inverts token meaning per mode: `white` = text, `black` = page - background. */ :root[data-theme='dark'] { - --sl-color-accent-low: color-mix(in oklch, var(--se-primary) 32%, var(--se-neutral-950)); - --sl-color-accent: var(--se-primary-dark); - --sl-color-accent-high: color-mix(in oklch, var(--se-primary) 45%, white); - - --sl-color-white: var(--se-neutral-100); - --sl-color-gray-1: var(--se-neutral-200); - --sl-color-gray-2: var(--se-neutral-300); - --sl-color-gray-3: var(--se-neutral-400); - --sl-color-gray-4: var(--se-neutral-600); - --sl-color-gray-5: var(--se-neutral-800); /* hairlines ≈ .dark --border */ - --sl-color-gray-6: var(--se-card-dark); /* raised surfaces / code blocks */ - --sl-color-gray-7: var(--se-sidebar-dark); - --sl-color-black: var(--se-neutral-950); - - --se-shadow-float: 0 1px 0 rgb(255 255 255 / 0.04) inset, 0 6px 16px rgb(0 0 0 / 0.45); /* .dark --shadow-float */ + --se-background: oklch(0.1684 0.005 250); + --se-foreground: oklch(0.9683 0.0017 250); + --se-card: oklch(0.2146 0.006 250); + --se-popover: oklch(0.2686 0.0076 250); + --se-primary: oklch(0.56 0.1869 265.73); + --se-primary-foreground: oklch(1 0 0); + --se-muted-foreground: oklch(0.7107 0.0055 250); + --se-destructive: oklch(0.68 0.21 17.81); + --se-success: oklch(0.797 0.1683 156.11); + --se-attention: oklch(0.7272 0.177 54.06); + --se-border: oklch(1 0 0 / 8%); + --se-ring: oklch(0.56 0.1869 265.73); + --se-shadow-float: 0 1px 0 rgb(255 255 255 / 0.04) inset, 0 6px 16px rgb(0 0 0 / 0.45); + --se-sidebar: oklch(0.19 0.005 250); + --se-sidebar-accent: oklch(1 0 0 / 6%); + --se-sidebar-border: var(--se-border); } -:root[data-theme='light'] { - --sl-color-accent-low: color-mix(in oklch, var(--se-primary) 16%, white); - /* Brand primary darkened for AA text contrast on light surfaces (#5684FD - alone is ~3.2:1 on white); tints and dark mode keep the exact brand hue. */ - --sl-color-accent: oklch(0.52 0.1869 265.73); - --sl-color-accent-high: oklch(0.38 0.15 265.73); - --sl-color-white: var(--se-neutral-900); +:root[data-theme='light'] { + --sl-color-white: var(--se-foreground); --sl-color-gray-1: var(--se-neutral-800); --sl-color-gray-2: var(--se-neutral-700); --sl-color-gray-3: var(--se-neutral-500); --sl-color-gray-4: var(--se-neutral-400); - --sl-color-gray-5: var(--se-neutral-200); /* hairlines = light --border */ - --sl-color-gray-6: var(--se-neutral-100); /* inline code bg = light --well */ - --sl-color-gray-7: var(--se-neutral-50); /* subtle bg = light --background */ - --sl-color-black: var(--se-neutral-0); /* page bg = light --card */ - - --se-shadow-float: 0 4px 12px rgb(0 0 0 / 0.09), 0 1px 3px rgb(0 0 0 / 0.06); /* --shadow-float */ + --sl-color-gray-5: var(--se-neutral-200); + --sl-color-gray-6: var(--se-neutral-100); + --sl-color-gray-7: var(--se-background); + --sl-color-black: var(--se-card); + --sl-color-accent: var(--se-primary); + --sl-color-accent-low: color-mix(in srgb, var(--se-primary) 10%, var(--se-sidebar)); + --sl-color-accent-high: oklch(0.38 0.15 265.73); + --sl-color-text-accent: var(--se-primary); +} +:root[data-theme='dark'] { + --sl-color-white: var(--se-foreground); + --sl-color-gray-1: var(--se-neutral-200); + --sl-color-gray-2: var(--se-neutral-300); + --sl-color-gray-3: var(--se-muted-foreground); + --sl-color-gray-4: var(--se-neutral-600); + --sl-color-gray-5: var(--se-neutral-800); + --sl-color-gray-6: var(--se-card); + --sl-color-gray-7: var(--se-sidebar); + --sl-color-black: var(--se-background); + --sl-color-accent: var(--se-primary); + --sl-color-accent-low: color-mix(in srgb, var(--se-primary) 22%, var(--se-sidebar)); + /* Text tint for readable links; filled controls use the exact UI primary. */ + --sl-color-accent-high: color-mix(in srgb, var(--se-primary) 55%, white); + --sl-color-text-accent: var(--sl-color-accent-high); +} +:root[data-theme] { + --sl-color-bg: var(--se-background); + --sl-color-bg-nav: var(--se-sidebar); + --sl-color-bg-sidebar: var(--se-sidebar); + --sl-color-hairline-light: var(--se-border); + --sl-color-hairline: var(--se-border); + --sl-color-text: var(--se-foreground); +} +.sl-link-button.primary { + background: var(--se-primary); + border-color: var(--se-primary); + color: var(--se-primary-foreground); } /* Inline code may wrap; block code wrapping is handled by Expressive Code @@ -98,6 +120,9 @@ .card, .sl-link-card { border-radius: var(--se-radius); + border-color: var(--se-border); + background-color: var(--se-card); + box-shadow: none; transition: border-color 0.2s ease, transform 0.2s ease, @@ -110,35 +135,14 @@ box-shadow: var(--se-shadow-float); } -/* Topic switcher: neutral icon chips at rest (muted icon on a subtly raised - surface, like the app sidebar), brand-primary tint only on hover/current — - color signals state, not category. */ -.starlight-sidebar-topics .starlight-sidebar-topics-icon { - border: none; - border-radius: var(--se-radius-sm); - padding: 0.3rem; - background-color: var(--sl-color-gray-6); - color: var(--sl-color-gray-3); - transition: background-color 0.15s ease, color 0.15s ease; -} -.starlight-sidebar-topics svg { - font-size: 1.05rem; -} -.starlight-sidebar-topics - a:is(.starlight-sidebar-topics-current, :hover, :focus-visible) - .starlight-sidebar-topics-icon { - background-color: color-mix(in srgb, var(--se-primary) 15%, transparent); - color: var(--sl-color-accent); -} - /* StreamElements command syntax blocks keep their signature orange accent bar (system attention orange). */ .expressive-code pre[data-language='streamelements'] { border-inline-start: 3px solid var(--se-attention); } -/* Keyboard shortcut keys. */ -kbd { +/* Keyboard shortcuts in docs content; Starlight owns its search shortcut. */ +.sl-markdown kbd { display: inline-block; padding: 0.1em 0.45em; border: 1px solid var(--sl-color-gray-4); @@ -150,3 +154,142 @@ kbd { font-size: 0.8em; line-height: 1.4; } + +@media (prefers-reduced-motion: reduce) { + .card, .sl-link-card { transition: none; } + .card:hover, .sl-link-card:hover { transform: none; } +} + +/* Semantic callouts and badges share the app palette. */ +:root[data-theme] { + --sl-color-blue: var(--se-primary); + --sl-color-blue-low: var(--sl-color-accent-low); + --sl-color-blue-high: var(--sl-color-text-accent); + --sl-color-purple: var(--se-primary); + --sl-color-purple-low: var(--sl-color-accent-low); + --sl-color-purple-high: var(--sl-color-text-accent); + --sl-color-green: var(--se-success); + --sl-color-orange: var(--se-attention); + --sl-color-red: var(--se-destructive); + --sl-color-green-low: color-mix(in srgb, var(--se-success) 12%, var(--se-background)); + --sl-color-orange-low: color-mix(in srgb, var(--se-attention) 12%, var(--se-background)); + --sl-color-red-low: color-mix(in srgb, var(--se-destructive) 12%, var(--se-background)); +} +:root[data-theme='light'] { + --sl-color-green-high: color-mix(in srgb, var(--se-success) 40%, black); + --sl-color-orange-high: color-mix(in srgb, var(--se-attention) 45%, black); + --sl-color-red-high: color-mix(in srgb, var(--se-destructive) 75%, black); +} +:root[data-theme='dark'] { + --sl-color-green-high: color-mix(in srgb, var(--se-success) 65%, white); + --sl-color-orange-high: color-mix(in srgb, var(--se-attention) 65%, white); + --sl-color-red-high: color-mix(in srgb, var(--se-destructive) 65%, white); +} +.starlight-aside { border-radius: var(--se-radius-sm); } + +@media (max-width: 49.999rem) { + .site-title { font-size: clamp(1rem, 4.6vw, 1.25rem); } +} +@media (max-width: 40rem) { + /* Keep reference columns readable; Starlight's table scrolls within the page. */ + .sl-markdown-content table:has(tr > :nth-child(3)) :is(th, td) { + min-width: 9rem; + } +} + +/* Article hierarchy: keep headings distinct without dwarfing short sections. */ +:root[data-theme] { + --sl-text-h1: 2.25rem; + --sl-text-h2: 1.75rem; + --sl-text-h3: 1.375rem; + --sl-text-h4: 1.125rem; +} +@media (max-width: 49.999rem) { + :root[data-theme] { --sl-text-h1: 1.875rem; --sl-text-h2: 1.5rem; } +} +@media (min-width: 50rem) { + .site-title { font-size: 1.5rem; } +} +@media (max-width: 40rem) { + .sl-markdown-content table:has(tr > :nth-child(3)) :is(th, td):last-child { + min-width: 16rem; + } +} + +.pagination-links a { + background: var(--se-card); + border-color: var(--se-border); + border-radius: var(--se-radius); + box-shadow: none; + font-size: 0.875rem; +} +.pagination-links .link-title { font-size: 1.125rem; font-weight: 600; } +.pagination-links a:hover { border-color: var(--se-primary); } + +/* Header: quieter branding and consistent controls. */ +.header .site-title { + color: var(--se-foreground); + font-size: 1.25rem; + letter-spacing: -0.025em; +} +.header .right-group, .header .social-icons { gap: 0.5rem; } +.header .social-icons a { + display: grid; + place-items: center; + width: 2.5rem; + height: 2.5rem; + padding: 0; + margin: 0; + border-radius: var(--se-radius-sm); + color: var(--se-muted-foreground); +} +.header .social-icons a:hover { + background: var(--se-sidebar-accent); + color: var(--se-foreground); +} +.header .social-icons::after { height: 1.5rem; border-color: var(--se-border); } +.header select { min-height: 2.5rem; } +.header a:focus-visible, .header select:focus-visible { + outline: 2px solid var(--se-ring); + outline-offset: 2px; +} +@media (min-width: 50rem) and (max-width: 63.999rem) { + .header .social-icons a { width: 2rem; } +} +@media (max-width: 49.999rem) { + .header .site-title { font-size: clamp(1rem, 4.6vw, 1.125rem); } + :root[data-theme] { --sl-menu-button-size: 2.5rem; } + starlight-menu-button button, + starlight-menu-button[aria-expanded='true'] button { + background: var(--se-sidebar-accent); + color: var(--se-foreground); + border: 1px solid var(--se-border); + border-radius: var(--se-radius-sm); + box-shadow: none; + } +} +.header button[data-open-modal] { border-radius: var(--se-radius-sm); } +.header button[data-open-modal]:focus-visible { outline: 2px solid var(--se-ring); outline-offset: 2px; } +@media (min-width: 50rem) { + .header button[data-open-modal] { background: var(--se-card); border-color: var(--se-border); } +} +@media (max-width: 49.999rem) { + .header button[data-open-modal] { width: 2.5rem; justify-content: center; padding: 0; } +} + +/* Use the available content width, not the viewport, to choose card columns. + Keep at most two columns and give each card at least 18rem when possible. */ +.card-grid { + grid-template-columns: repeat(auto-fit, minmax(min(100%, max(18rem, calc(50% - 0.75rem))), 1fr)); +} +@media (max-width: 30rem) { + .hero .actions { + flex-direction: column; + align-items: stretch; + width: 100%; + max-width: 24rem; + margin-inline: auto; + gap: 0.75rem; + } + .hero .actions a { justify-content: center; } +}