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
21 changes: 20 additions & 1 deletion remotion/src/style/theme.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { ENTRANCES, MOTIONS, PACKS, ROLES, resolveTheme } from "./theme";
import { ENTRANCES, FONT_IDS, MOTIONS, PACKS, ROLES, resolveTheme } from "./theme";

describe("resolveTheme", () => {
it("returns null without a style", () => {
Expand Down Expand Up @@ -71,3 +71,22 @@ describe("motion presets", () => {
}
});
});

describe("fonts picked to match a reference", () => {
it("swap the display and label faces and leave the rest of the pack", () => {
const theme = resolveTheme({ pack: "clean", fonts: { display: "condensed", label: "mono" } });
expect(theme?.type.display.family).toContain("Barlow Condensed");
expect(theme?.type.label.family).toContain("Courier Prime");
expect(theme?.color).toEqual(PACKS.clean.color);
});

it("keep the pack's faces when none are picked", () => {
expect(resolveTheme({ pack: "editorial" })?.type).toEqual(PACKS.editorial.type);
});

it("offer every font on every pack", () => {
for (const font of FONT_IDS) {
expect(resolveTheme({ pack: "riso", fonts: { display: font } })?.type.display.family).toBeTruthy();
}
});
});
33 changes: 32 additions & 1 deletion remotion/src/style/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,14 @@ export const MOTIONS = {

export type MotionId = keyof typeof MOTIONS;

export type FontId = "grotesk" | "serif" | "condensed" | "mono";

export const FONT_IDS: FontId[] = ["grotesk", "serif", "condensed", "mono"];

export interface ThemeInput {
pack: PackId;
motion?: MotionId;
fonts?: { display?: FontId; label?: FontId };
overrides?: DeepPartial<Theme>;
textures?: Theme["textures"];
}
Expand Down Expand Up @@ -439,9 +444,35 @@ const deepMerge = <T>(base: T, patch: unknown): T => {
return out as T;
};

const FONTS: Record<FontId, { display: Face; label: Face }> = {
grotesk: {
display: face("DM Sans", 700, { tracking: -2.5, lineHeight: 1.02 }),
label: face("DM Sans", 700, { lineHeight: 1.1 }),
},
serif: {
display: face("Playfair Display", 900, { italic: true, tracking: -1, lineHeight: 1.02 }),
label: face("Playfair Display", 700, { italic: true, lineHeight: 1.1 }),
},
condensed: {
display: face("Barlow Condensed", 700, { tracking: -0.5, lineHeight: 0.95 }),
label: face("Barlow Condensed", 600, { tracking: 0.5, lineHeight: 1.05 }),
},
mono: {
display: face("Courier Prime", 400, { tracking: -1.5, lineHeight: 1.08, size: 0.78 }),
label: face("Courier Prime", 400, { lineHeight: 1.2 }),
},
};

const withFonts = (theme: Theme, fonts: ThemeInput["fonts"]): Theme => {
const display = fonts?.display && FONTS[fonts.display]?.display;
const label = fonts?.label && FONTS[fonts.label]?.label;
if (!display && !label) return theme;
return { ...theme, type: { ...theme.type, ...(display ? { display } : {}), ...(label ? { label } : {}) } };
};

export const resolveTheme = (input?: ThemeInput | null): Theme | null => {
if (!input) return null;
const pack: Theme = PACKS[input.pack] ?? PACKS.collage;
const pack: Theme = withFonts(PACKS[input.pack] ?? PACKS.collage, input.fonts);
const base = input.motion ? { ...pack, motion: MOTIONS[input.motion] ?? pack.motion } : pack;
const merged = deepMerge(base, input.overrides ?? {});
return { ...merged, textures: { ...merged.textures, ...(input.textures ?? {}) } };
Expand Down
Loading