Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/chrome-light-theme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@karnstack/kino": minor
---

Add a `chromeTheme` prop (`"light" | "dark"`, default `"dark"`) to the player and ScenesPlayer chrome. Light mode renders the chrome as light glass via a `data-kino-theme="light"` token set. Distinct from `sceneTheme`, which themes the scene stage inside the iframe.
38 changes: 34 additions & 4 deletions demo/player-studio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export function PlayerStudio({
const [source, setSource] = useState<string>(SAMPLES[0].id)
const [accent, setAccent] = useState<string>(DEFAULT_ACCENT)
const [radius, setRadius] = useState(DEFAULT_RADIUS)
const [chromeTheme, setChromeTheme] = useState<"light" | "dark">("dark")

// The Source picker swaps the Mux playback id; the embed providers each play a
// single fixed sample, so it only applies to Mux.
Expand Down Expand Up @@ -142,6 +143,7 @@ export function PlayerStudio({
srclang: "en",
}}
accentColor={accent}
chromeTheme={chromeTheme}
theme={{ "--kino-radius": `${radius}px` }}
/>
) : (
Expand Down Expand Up @@ -246,10 +248,7 @@ export function PlayerStudio({
</Control>
)}

<Control
label="Accent"
className={sourceVisible ? undefined : "sm:col-span-2"}
>
<Control label="Accent">
<div className="flex flex-wrap items-center gap-2.5">
{ACCENTS.map((a) => {
const active = accent === a.value
Expand Down Expand Up @@ -293,6 +292,37 @@ export function PlayerStudio({
</div>
</Control>

{/* Chrome light/dark stamps data-kino-theme on the .kino root. Only the
Scenes provider forwards chromeTheme today, so the swap is live on
the Scenes tab; the Mux/Vimeo/Native/YouTube wrappers do not accept
the prop yet (that passthrough lives in the library, not the demo). */}
<Control
label="Chrome"
className={sourceVisible ? "sm:col-span-2" : undefined}
>
<div className="flex flex-wrap gap-2">
{(["dark", "light"] as const).map((t) => {
const active = chromeTheme === t
return (
<button
key={t}
type="button"
onClick={() => setChromeTheme(t)}
aria-pressed={active}
className={[
"rounded-lg px-3 py-2 text-sm font-medium capitalize transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-leader",
active
? "bg-leader text-ink"
: "text-paper-dim ring-1 ring-white/12 hover:bg-white/5 hover:text-paper",
].join(" ")}
>
{t}
</button>
)
})}
</div>
</Control>

{showRadius && (
<Control label="Corner radius" className="sm:col-span-2">
<div className="flex items-center gap-3">
Expand Down
8 changes: 8 additions & 0 deletions src/scenes/scenes-player.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import {
export type ScenesPlayerProps = Omit<ScenesProviderOptions, "theme"> & {
accentColor?: string
theme?: Record<string, string>
/**
* Chrome theme for the parent-document controls; defaults to dark. Stamped
* as `data-kino-theme` on the `.kino` root. Distinct from `sceneTheme`,
* which themes the iframe stage.
*/
chromeTheme?: "light" | "dark"
/**
* Stage theme inside the host document; defaults to dark. The initial
* value seeds the host, later values flip it live without a remount.
Expand All @@ -38,6 +44,7 @@ export function ScenesPlayer(props: ScenesPlayerProps) {
function ScenesPlayerInner({
accentColor,
theme,
chromeTheme,
sceneTheme,
className,
placeholder,
Expand All @@ -58,6 +65,7 @@ function ScenesPlayerInner({
provider={providerRef.current}
accentColor={accentColor}
theme={theme}
chromeTheme={chromeTheme}
className={className}
placeholder={placeholder}
>
Expand Down
Loading
Loading