Skip to content

Commit d89ffc6

Browse files
authored
Merge pull request #118 from peelar/vk/453c-task-1-2-expand
Add Friendly (Nunito) and Premium (DM Serif Display) font styles (Vibe Kanban)
2 parents b2b15c9 + ea5ab53 commit d89ffc6

15 files changed

Lines changed: 325 additions & 27 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"dopeshot-app": patch
3+
---
4+
5+
Add Friendly (Nunito) and Premium (DM Serif Display) font styles for brand users, including tier-aware font selection and updated adaptive typography rules.

apps/app/src/app/(playground)/_components/playground-page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,7 @@ function PlaygroundPageInner({
443443
onUploadClick={openFilePicker}
444444
isProcessingUpload={isProcessingUpload}
445445
showUploadButton={requiresScreenshot}
446+
isBrandUser={isBrandUser}
446447
onUploadAsset={handleFileProcess}
447448
/>
448449
) : null}

apps/app/src/app/layout.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import {
55
Commissioner,
66
IBM_Plex_Mono,
77
Bricolage_Grotesque,
8-
Rubik,
98
Chivo,
109
Chakra_Petch,
11-
Playfair_Display,
10+
Nunito,
11+
DM_Serif_Display,
1212
} from "next/font/google";
1313
import Script from "next/script";
1414
import "./globals.css";
@@ -50,9 +50,9 @@ const bricolageGrotesque = Bricolage_Grotesque({
5050
variable: "--font-bold",
5151
});
5252

53-
const rubik = Rubik({
53+
const nunito = Nunito({
5454
subsets: ["latin"],
55-
weight: ["400", "600"],
55+
weight: ["400", "600", "700"],
5656
variable: "--font-friendly",
5757
});
5858

@@ -68,8 +68,9 @@ const chakraPetch = Chakra_Petch({
6868
variable: "--font-technical",
6969
});
7070

71-
const playfairDisplay = Playfair_Display({
71+
const dmSerifDisplay = DM_Serif_Display({
7272
subsets: ["latin"],
73+
weight: ["400"],
7374
variable: "--font-premium",
7475
});
7576

@@ -80,10 +81,10 @@ const fontVariables = [
8081
commissioner.variable,
8182
ibmPlexMono.variable,
8283
bricolageGrotesque.variable,
83-
rubik.variable,
84+
nunito.variable,
8485
chivo.variable,
8586
chakraPetch.variable,
86-
playfairDisplay.variable,
87+
dmSerifDisplay.variable,
8788
].join(" ");
8889

8990
const siteUrl = "https://app.dopeshot.io";

apps/app/src/components/config/layout-config.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,14 @@ import { track } from "@/lib/analytics";
2525
interface LayoutConfigProps {
2626
onUploadAsset?: (file: File, kind: "screenshot" | "logo" | "background") => void;
2727
isMobile?: boolean;
28+
isBrandUser?: boolean;
2829
}
2930

30-
export const LayoutConfigPanel = ({ onUploadAsset, isMobile = false }: LayoutConfigProps) => {
31+
export const LayoutConfigPanel = ({
32+
onUploadAsset,
33+
isMobile = false,
34+
isBrandUser = false,
35+
}: LayoutConfigProps) => {
3136
const config = useAtomValue(configAtom);
3237
const setConfig = useSetAtom(configAtom);
3338
const assets = useAtomValue(assetsAtom);
@@ -187,7 +192,7 @@ export const LayoutConfigPanel = ({ onUploadAsset, isMobile = false }: LayoutCon
187192
<div className="flex w-full items-center justify-between">
188193
<span className="text-sm font-semibold">Text</span>
189194
</div>
190-
<LayoutSection />
195+
<LayoutSection isBrandUser={isBrandUser} />
191196
</section>
192197
)}
193198

apps/app/src/components/layout/mobile-actions.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ interface MobileActionsProps {
2626
onUploadClick: () => void;
2727
isProcessingUpload: boolean;
2828
showUploadButton: boolean;
29+
isBrandUser?: boolean;
2930
onUploadAsset: (file: File, kind: "screenshot" | "logo" | "background") => void;
3031
}
3132

@@ -38,6 +39,7 @@ export function MobileActions({
3839
onUploadClick,
3940
isProcessingUpload,
4041
showUploadButton,
42+
isBrandUser = false,
4143
onUploadAsset,
4244
}: MobileActionsProps) {
4345
const sheetRef = useRef<HTMLDivElement>(null);
@@ -200,7 +202,11 @@ export function MobileActions({
200202
/>
201203
</div>
202204
<div className="mt-4 h-[calc(100%-48px)] overflow-y-auto">
203-
<LayoutConfigPanel onUploadAsset={onUploadAsset} isMobile={true} />
205+
<LayoutConfigPanel
206+
onUploadAsset={onUploadAsset}
207+
isMobile={true}
208+
isBrandUser={isBrandUser}
209+
/>
204210
</div>
205211
</SheetContent>
206212
</Sheet>

apps/app/src/components/layout/sidebar-tabs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export function SidebarTabs({ onUploadAsset, onFeedbackClick }: SidebarTabsProps
7070
{/* Tab content - scrollable */}
7171
<div className="flex-1 min-h-0 min-w-0 overflow-y-auto overflow-x-hidden">
7272
{activeTab === "design" ? (
73-
<LayoutConfigPanel onUploadAsset={onUploadAsset} />
73+
<LayoutConfigPanel onUploadAsset={onUploadAsset} isBrandUser={isBrandUser} />
7474
) : (
7575
<BrandPanel />
7676
)}

apps/app/src/components/selectors/font-style-selector.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use client";
22

33
import { FontStyle } from "@/domain/layout/types";
4-
import { FONT_STYLES } from "@/domain/layout/fonts";
4+
import { BRAND_FONTS, FONT_STYLES, FREE_FONTS } from "@/domain/layout/fonts";
55
import {
66
Select,
77
SelectContent,
@@ -13,10 +13,16 @@ import {
1313
interface FontStyleSelectorProps {
1414
fontStyle: FontStyle;
1515
onFontStyleChange: (fontStyle: FontStyle) => void;
16+
isBrandUser?: boolean;
1617
}
1718

18-
export function FontStyleSelector({ fontStyle, onFontStyleChange }: FontStyleSelectorProps) {
19+
export function FontStyleSelector({
20+
fontStyle,
21+
onFontStyleChange,
22+
isBrandUser = false,
23+
}: FontStyleSelectorProps) {
1924
const currentStyle = FONT_STYLES.find((s) => s.id === fontStyle);
25+
const availableStyles = isBrandUser ? BRAND_FONTS : FREE_FONTS;
2026

2127
return (
2228
<div className="w-full">
@@ -33,7 +39,7 @@ export function FontStyleSelector({ fontStyle, onFontStyleChange }: FontStyleSel
3339
</SelectValue>
3440
</SelectTrigger>
3541
<SelectContent className="min-w-48">
36-
{FONT_STYLES.map((style) => (
42+
{availableStyles.map((style) => (
3743
<SelectItem key={style.id} value={style.id} className="py-2">
3844
<div className="flex flex-col gap-1">
3945
<span className="font-medium text-sm">{style.name}</span>

apps/app/src/components/sidebar/layout-section.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ import { Label } from "@/components/ui/label";
88
import { FontStyleSelector } from "@/components/selectors/font-style-selector";
99
import type { FontStyle } from "@/domain/layout/types";
1010

11-
export function LayoutSection() {
11+
interface LayoutSectionProps {
12+
isBrandUser?: boolean;
13+
}
14+
15+
export function LayoutSection({ isBrandUser = false }: LayoutSectionProps) {
1216
const config = useAtomValue(configAtom);
1317
const setConfig = useSetAtom(configAtom);
1418
const orientation = useAtomValue(orientationAtom);
@@ -86,6 +90,7 @@ export function LayoutSection() {
8690
<FontStyleSelector
8791
fontStyle={config.fontStyle}
8892
onFontStyleChange={handleFontStyleChange}
93+
isBrandUser={isBrandUser}
8994
/>
9095
</div>
9196
)}

apps/app/src/domain/layout/adaptive-typography.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import type { FontStyle } from "./types";
1010
* - Founder: Balanced, neutral, max 3 lines
1111
* - Billboard: Bold, expressive, max 2 lines
1212
* - Terminal: Compact, technical, max 4 lines
13-
* - Editorial: Elegant, serif, max 2 lines
13+
* - Friendly: Warm, rounded, max 3 lines
14+
* - Premium: Display serif, max 2 lines
1415
*/
1516

1617
export interface TypographyScalingRules {
@@ -70,6 +71,28 @@ export const FONT_STYLE_SCALING_RULES: Record<FontStyle, TypographyScalingRules>
7071
titleLetterSpacing: -0.01,
7172
subtitleLetterSpacing: -0.01,
7273
},
74+
friendly: {
75+
titleMinSize: 2.25,
76+
titleMaxSize: 4.25,
77+
subtitleMinSize: 1.0,
78+
subtitleMaxSize: 1.4,
79+
titleLineHeight: 1.12,
80+
subtitleLineHeight: 1.45,
81+
titleMaxLines: 3,
82+
subtitleMaxLines: 3,
83+
titleLetterSpacing: -0.01,
84+
},
85+
premium: {
86+
titleMinSize: 2.5,
87+
titleMaxSize: 4.75,
88+
subtitleMinSize: 1.0,
89+
subtitleMaxSize: 1.35,
90+
titleLineHeight: 1.02,
91+
subtitleLineHeight: 1.4,
92+
titleMaxLines: 2,
93+
subtitleMaxLines: 2,
94+
titleLetterSpacing: -0.01,
95+
},
7396

7497
};
7598

@@ -92,7 +115,6 @@ export function getTitleClasses(fontStyle: FontStyle, textLength?: number): stri
92115
const rules = FONT_STYLE_SCALING_RULES[fontStyle];
93116

94117
const baseClasses = [
95-
"font-bold",
96118
"text-balance",
97119
"overflow-wrap-anywhere",
98120
];
@@ -104,9 +126,11 @@ export function getTitleClasses(fontStyle: FontStyle, textLength?: number): stri
104126

105127
// Add style-specific classes
106128
const styleClasses: Record<FontStyle, string[]> = {
107-
founder: ["tracking-tight"],
129+
founder: ["tracking-tight", "font-bold"],
108130
billboard: ["tracking-tighter", "font-extrabold"],
109-
terminal: ["tracking-tight", "font-mono"],
131+
terminal: ["tracking-tight", "font-bold", "font-mono"],
132+
friendly: ["tracking-tight", "font-semibold"],
133+
premium: ["tracking-tighter", "font-normal"],
110134
};
111135

112136
baseClasses.push(...styleClasses[fontStyle]);
@@ -136,6 +160,8 @@ export function getSubtitleClasses(fontStyle: FontStyle, textLength?: number): s
136160
founder: ["tracking-normal"],
137161
billboard: ["tracking-tight", "font-medium"],
138162
terminal: ["tracking-tight", "font-mono"],
163+
friendly: ["tracking-normal", "font-normal"],
164+
premium: ["tracking-tight", "font-normal"],
139165
};
140166

141167
baseClasses.push(...styleClasses[fontStyle]);

apps/app/src/domain/layout/fonts.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ export interface FontStyleDefinition {
1616

1717
/**
1818
* Font Styles for Free Users
19-
* Four opinionated typographic systems that adapt automatically
19+
* Opinionated typographic systems that adapt automatically.
20+
*
21+
* Free users can select a subset (see `FREE_FONTS`).
2022
*/
2123
export const FONT_STYLES: FontStyleDefinition[] = [
2224
{
@@ -43,10 +45,32 @@ export const FONT_STYLES: FontStyleDefinition[] = [
4345
cssVariable: "--font-developer",
4446
description: "Monospace, technical, tool-like",
4547
},
48+
{
49+
id: "friendly",
50+
name: "Friendly",
51+
fontName: "Nunito",
52+
foundry: "Google Fonts",
53+
cssVariable: "--font-friendly",
54+
description: "Rounded, upbeat, approachable",
55+
},
56+
{
57+
id: "premium",
58+
name: "Premium",
59+
fontName: "DM Serif Display",
60+
foundry: "Colophon Foundry",
61+
cssVariable: "--font-premium",
62+
description: "High-contrast, editorial, luxury",
63+
},
4664
];
4765

4866
export const DEFAULT_FONT_STYLE: FontStyle = "founder";
4967

68+
export const FREE_FONTS: FontStyleDefinition[] = FONT_STYLES.filter(
69+
(style) => style.id === "founder" || style.id === "billboard" || style.id === "terminal",
70+
);
71+
72+
export const BRAND_FONTS: FontStyleDefinition[] = FONT_STYLES;
73+
5074
/**
5175
* Get font style definition by ID
5276
*/
@@ -178,8 +202,8 @@ export const FONTS: FontDefinition[] = [
178202
{
179203
id: "friendly",
180204
alias: "Buddy",
181-
fontName: "Rubik",
182-
foundry: "Hubert & Fischer",
205+
fontName: "Nunito",
206+
foundry: "Google Fonts",
183207
cssVariable: "--font-friendly",
184208
},
185209
{
@@ -199,8 +223,8 @@ export const FONTS: FontDefinition[] = [
199223
{
200224
id: "premium",
201225
alias: "BlackTie",
202-
fontName: "Playfair Display",
203-
foundry: "Claus Eggers Sorensen",
226+
fontName: "DM Serif Display",
227+
foundry: "Colophon Foundry",
204228
cssVariable: "--font-premium",
205229
},
206230
];

0 commit comments

Comments
 (0)