Skip to content
Open
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
6 changes: 4 additions & 2 deletions src/components/launcher/MainLaunchButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -387,8 +387,10 @@ export function MainLaunchButton({

const displaySubText = statusSubText || selectedVersionLabel;
return (
<div className="w-full flex flex-col items-center justify-center leading-none -mt-4">
<span className="text-5xl text-center lowercase">{actionText}</span>{" "}
<div className="launch-btn w-full flex flex-col items-center justify-center leading-none -mt-4">
<span className="font-minecraft text-5xl text-center lowercase">
{actionText}
</span>{" "}
{displaySubText && (
<span
className={cn(
Expand Down
2 changes: 1 addition & 1 deletion src/components/modrinth/v2/ModrinthSearchControlsV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export const ModrinthSearchControlsV2: React.FC<
onClick={() => onProjectTypeChange(type)}
variant={projectType === type ? "flat" : "ghost"}
size={buttonSize}
className={`flex-1 min-w-0 text-[1.7em]`}
className={`flex-1 min-w-0 text-2xl`}
>
{type}s
</Button>
Expand Down
146 changes: 141 additions & 5 deletions src/components/tabs/SettingsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ export function SettingsTab() {
} = useThemeStore();
const { currentEffect, setCurrentEffect } = useBackgroundEffectStore();
const { qualityLevel, setQualityLevel } = useQualitySettingsStore();
const {
headerFontPreset,
textFontPreset,
setHeaderFontPreset,
setTextFontPreset,
} = useThemeStore();

const { confirm, confirmDialog } = useConfirmDialog();

Expand Down Expand Up @@ -478,16 +484,143 @@ export function SettingsTab() {
size="md"
>
Download
</Button> </div>
</Button>
</div>
</div>
</Card>

<Card variant="flat" className="p-6">
<div className="mb-4">
<div className="flex items-center gap-2 mb-2">
<Icon icon="solar:text-bold" className="w-6 h-6 text-white" />
<h3 className="text-3xl font-minecraft text-white lowercase">
Typography
</h3>
</div>
<p className="text-base text-white/70 font-minecraft-ten mt-2">
Choose fonts and base size used throughout the launcher
</p>
</div>

{(() => {
const options = [
{
value: "minecraft" as const,
label: "Minecraft (default)",
headerSample: 'Minecraft, monospace',
textSample:
'MinecraftTen, ui-sans-serif, system-ui, "Segoe UI", Inter, sans-serif',
},
{
value: "system" as const,
label: "System Sans",
headerSample:
'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Inter, "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue", Arial, sans-serif',
textSample:
'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Inter, "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue", Arial, sans-serif',
},
{
value: "monospace" as const,
label: "Monospace",
headerSample:
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "DejaVu Sans Mono", "Cascadia Mono", "Fira Code", "Courier New", monospace',
textSample:
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "DejaVu Sans Mono", "Cascadia Mono", "Fira Code", "Courier New", monospace',
},
];

return (
<div className="space-y-6">
<div>
<h4 className="text-xl font-minecraft text-white lowercase mb-3">Header font</h4>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{options.map((opt) => (
<Card
key={`header-${opt.value}`}
variant="flat"
className={cn(
"relative cursor-pointer transition-all duration-300 p-4",
headerFontPreset === opt.value
? "ring-2 ring-white/30"
: "hover:bg-black/40",
)}
onClick={() => setHeaderFontPreset(opt.value)}
>
<div className="flex flex-col gap-2">
<p className="text-xl text-white lowercase" style={{ fontFamily: opt.textSample }}>{opt.label}</p>
<div className="rounded-md p-3 bg-black/40 border border-white/10">
<div
className="text-white text-base mb-1"
style={{ fontFamily: opt.headerSample }}
>
Heading Sample
</div>
</div>
</div>
{headerFontPreset === opt.value && (
<div className="absolute top-2 right-2">
<Icon
icon="solar:check-circle-bold"
className="w-5 h-5"
style={{ color: accentColor.value }}
/>
</div>
)}
</Card>
))}
</div>
</div>

<div>
<h4 className="text-xl font-minecraft text-white lowercase mb-3">Text font</h4>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{options.map((opt) => (
<Card
key={`text-${opt.value}`}
variant="flat"
className={cn(
"relative cursor-pointer transition-all duration-300 p-4",
textFontPreset === opt.value
? "ring-2 ring-white/30"
: "hover:bg-black/40",
)}
onClick={() => setTextFontPreset(opt.value)}
>
<div className="flex flex-col gap-2">
<p className="text-xl text-white lowercase" style={{ fontFamily: opt.textSample }}>{opt.label}</p>
<div className="rounded-md p-3 bg-black/40 border border-white/10">
<div
className="text-white/80 text-xs"
style={{ fontFamily: opt.textSample }}
>
Lorem ipsum dolor sit amet
</div>
</div>
</div>
{textFontPreset === opt.value && (
<div className="absolute top-2 right-2">
<Icon
icon="solar:check-circle-bold"
className="w-5 h-5"
style={{ color: accentColor.value }}
/>
</div>
)}
</Card>
))}
</div>
</div>
</div>
);
})()}
</Card>

<Card variant="flat" className="p-6">
<div className="flex items-center gap-2 mb-4">
<Icon icon="solar:palette-bold" className="w-5 h-5 text-white" />
<h4 className="text-2xl font-minecraft text-white lowercase">
<h3 className="text-3xl font-minecraft text-white lowercase">
Custom Colors
</h4>
</h3>
</div>
<p className="text-sm text-white/70 font-minecraft-ten mb-4">
Create your own custom accent color
Expand Down Expand Up @@ -536,8 +669,11 @@ export function SettingsTab() {
/>
))}
</div>
</div> )} </div>
</Card> <Card variant="flat" className="p-6">
</div>
)}
</div>
</Card>
<Card variant="flat" className="p-6">
<div className="mb-4">
<div className="flex items-center gap-2 mb-2">
<Icon icon="solar:widget-bold" className="w-6 h-6 text-white" />
Expand Down
45 changes: 45 additions & 0 deletions src/store/useThemeStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@ interface ThemeState {
setAccentColor: (color: AccentColor) => void;
setCustomAccentColor: (hexColor: string) => void;
applyAccentColorToDOM: () => void;
headerFontPreset: FontPreset;
textFontPreset: FontPreset;
setHeaderFontPreset: (preset: FontPreset) => void;
setTextFontPreset: (preset: FontPreset) => void;
applyFontPresetsToDOM: () => void;
customColorHistory: string[];
addToCustomColorHistory: (hexColor: string) => void;
clearCustomColorHistory: () => void;
Expand All @@ -232,6 +237,8 @@ export const useThemeStore = create<ThemeState>()(
persist(
(set, get) => ({
accentColor: ACCENT_COLORS.blue,
headerFontPreset: 'minecraft',
textFontPreset: 'minecraft',
isBackgroundAnimationEnabled: false,
isDetailViewSidebarOnLeft: true,
profileGroupingCriterion: "group",
Expand All @@ -252,6 +259,40 @@ export const useThemeStore = create<ThemeState>()(
get().applyBorderRadiusToDOM();
},

setHeaderFontPreset: (preset: FontPreset) => {
set({ headerFontPreset: preset });
get().applyFontPresetsToDOM();
},

setTextFontPreset: (preset: FontPreset) => {
set({ textFontPreset: preset });
get().applyFontPresetsToDOM();
},

applyFontPresetsToDOM: () => {
const { headerFontPreset, textFontPreset } = get();
const getFont = (preset: FontPreset, target: 'header' | 'text') => {
switch (preset) {
case 'minecraft':
return target === 'header'
? '"Minecraft", monospace'
: '"MinecraftTen", sans-serif';
case 'system':
return 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Inter, "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue", Arial, sans-serif';
case 'monospace':
return 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "DejaVu Sans Mono", "Cascadia Mono", "Fira Code", "Courier New", monospace';
default:
return '"Minecraft", monospace';
}
};
const headerFont = getFont(headerFontPreset, 'header');
const textFont = getFont(textFontPreset, 'text');
document.documentElement.style.setProperty('--font-minecraft', headerFont);
document.documentElement.style.setProperty('--font-minecraft-ten', textFont);
// Expose current header font preset
document.documentElement.setAttribute('data-header-font', headerFontPreset);
},

setCustomAccentColor: (hexColor: string) => {
const colorVariants = calculateColorVariants(hexColor);
const customColor: AccentColor = {
Expand Down Expand Up @@ -385,6 +426,7 @@ export const useThemeStore = create<ThemeState>()(

state.applyAccentColorToDOM();
state.applyBorderRadiusToDOM();
state.applyFontPresetsToDOM?.();
// Ensure collapsedProfileGroups exists after rehydrate
if (!Array.isArray(state.collapsedProfileGroups)) {
state.collapsedProfileGroups = [];
Expand All @@ -394,3 +436,6 @@ export const useThemeStore = create<ThemeState>()(
},
),
);

// Font presets for appearance settings
export type FontPreset = 'minecraft' | 'system' | 'monospace';
33 changes: 32 additions & 1 deletion src/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@
background-size: cover;
background-position: center;
background-attachment: fixed;
font-family: var(--font-minecraft), monospace;
font-family: var(--font-minecraft-ten), sans-serif;
letter-spacing: 0.03em;
}

Expand Down Expand Up @@ -247,6 +247,37 @@
letter-spacing: 0.03em;
}

/* Normalize heading visual size for non-Minecraft presets */
:root[data-header-font='system'] .text-6xl.font-minecraft,
:root[data-header-font='monospace'] .text-6xl.font-minecraft {
font-size: 2.5rem;
}
:root[data-header-font='system'] .text-5xl.font-minecraft,
:root[data-header-font='monospace'] .text-5xl.font-minecraft {
font-size: 2rem;
}
:root[data-header-font='system'] .text-4xl.font-minecraft,
:root[data-header-font='monospace'] .text-4xl.font-minecraft {
font-size: 1.7rem;
}
:root[data-header-font='system'] .text-3xl.font-minecraft,
:root[data-header-font='monospace'] .text-3xl.font-minecraft {
font-size: 1.2rem;
}
:root[data-header-font='system'] .text-2xl.font-minecraft,
:root[data-header-font='monospace'] .text-2xl.font-minecraft {
font-size: 1.1rem;
}
:root[data-header-font='system'] .text-xl.font-minecraft,
:root[data-header-font='monospace'] .text-xl.font-minecraft {
font-size: 0.9rem;
}
:root[data-header-font='system'] .launch-btn,
:root[data-header-font='monospace'] .launch-btn {
/* Reset margin for launch buttons for non Minecraft presets */
margin-top: 0;
}

.text-shadow {
text-shadow: 0 2px 6px rgba(0, 0, 0, 0.9), 0 0 3px rgba(0, 0, 0, 1);
}
Expand Down