fix: add light theme support to connection page

Add isLight-aware styling to all block renderers (cards, timeline,
accordion, minimal), BlockButtons, and InstallationGuide. Update
colorParser gradient functions to generate light-appropriate backgrounds.
Reduce wave blob opacity on light theme for better content readability.
This commit is contained in:
c0mrade
2026-02-06 18:30:19 +03:00
parent 5171890745
commit 88d9377adb
9 changed files with 83 additions and 21 deletions

View File

@@ -8,6 +8,7 @@ import type {
RemnawavePlatformData,
RemnawaveButtonClient,
} from '@/types';
import { useTheme } from '@/hooks/useTheme';
import { CardsBlock, TimelineBlock, AccordionBlock, MinimalBlock, BlockButtons } from './blocks';
import type { BlockRendererProps } from './blocks';
@@ -51,6 +52,7 @@ export default function InstallationGuide({
onGoBack,
}: Props) {
const { t, i18n } = useTranslation();
const { isLight } = useTheme();
const detectedPlatform = useMemo(() => detectPlatform(), []);
const isMobile = typeof window !== 'undefined' && window.innerWidth < 768;
@@ -127,6 +129,7 @@ export default function InstallationGuide({
<BlockButtons
buttons={buttons}
variant={variant}
isLight={isLight}
subscriptionUrl={appConfig.subscriptionUrl}
hideLink={appConfig.hideLink}
deepLink={selectedApp?.deepLink}
@@ -140,6 +143,7 @@ export default function InstallationGuide({
appConfig.subscriptionUrl,
appConfig.hideLink,
selectedApp?.deepLink,
isLight,
getLocalizedText,
getBaseTranslation,
getSvgHtml,
@@ -221,9 +225,11 @@ export default function InstallationGuide({
if (app) setSelectedApp(app);
}
}}
className={`appearance-none rounded-xl border border-dark-700 bg-dark-800 py-2 pr-8 text-sm font-medium text-dark-200 outline-none transition-colors hover:border-dark-600 ${
currentPlatformSvg ? 'pl-10' : 'pl-4'
}`}
className={`appearance-none rounded-xl border py-2 pr-8 text-sm font-medium outline-none transition-colors ${
isLight
? 'border-dark-700/60 bg-white/80 text-dark-200 shadow-sm hover:border-dark-600'
: 'border-dark-700 bg-dark-800 text-dark-200 hover:border-dark-600'
} ${currentPlatformSvg ? 'pl-10' : 'pl-4'}`}
>
{availablePlatforms.map((p) => (
<option key={p} value={p}>
@@ -258,8 +264,12 @@ export default function InstallationGuide({
onClick={() => setSelectedApp(app)}
className={`relative flex min-w-[calc(50%-0.25rem)] items-center gap-2 overflow-hidden rounded-xl px-4 py-2 text-sm font-medium transition-all active:scale-[0.97] ${
isSelected
? 'bg-accent-500/15 text-accent-400 ring-1 ring-accent-500/40'
: 'border border-dark-700/50 bg-dark-800/80 text-dark-200 hover:border-dark-600/50 hover:bg-dark-700/80'
? isLight
? 'bg-accent-500/15 text-accent-600 ring-1 ring-accent-500/40'
: 'bg-accent-500/15 text-accent-400 ring-1 ring-accent-500/40'
: isLight
? 'border border-dark-700/60 bg-white/80 text-dark-200 shadow-sm hover:border-dark-600/50 hover:bg-white'
: 'border border-dark-700/50 bg-dark-800/80 text-dark-200 hover:border-dark-600/50 hover:bg-dark-700/80'
}`}
>
{app.featured && <span className="h-2 w-2 shrink-0 rounded-full bg-amber-400" />}
@@ -306,6 +316,7 @@ export default function InstallationGuide({
<Renderer
blocks={selectedApp.blocks}
isMobile={isMobile}
isLight={isLight}
getLocalizedText={getLocalizedText}
getSvgHtml={getSvgHtml}
renderBlockButtons={renderBlockButtons}

View File

@@ -6,6 +6,7 @@ import type { BlockRendererProps } from './types';
export function AccordionBlock({
blocks,
isMobile,
isLight,
getLocalizedText,
getSvgHtml,
renderBlockButtons,
@@ -15,14 +16,20 @@ export function AccordionBlock({
return (
<div className="space-y-2">
{blocks.map((block, index) => {
const gradientStyle = getColorGradient(block.svgIconColor || 'cyan');
const gradientStyle = getColorGradient(block.svgIconColor || 'cyan', isLight);
const isOpen = openIndex === index;
return (
<div
key={index}
className={`overflow-hidden rounded-2xl border bg-dark-800/50 transition-colors ${
isOpen ? 'border-accent-500/30' : 'border-dark-700/50'
className={`overflow-hidden rounded-2xl border transition-colors ${
isLight
? isOpen
? 'border-accent-500/30 bg-white/80 shadow-sm'
: 'border-dark-700/60 bg-white/60'
: isOpen
? 'border-accent-500/30 bg-dark-800/50'
: 'border-dark-700/50 bg-dark-800/50'
}`}
>
{/* Control */}

View File

@@ -38,6 +38,7 @@ const CheckIcon = () => (
interface BlockButtonsProps {
buttons: RemnawaveButtonClient[] | undefined;
variant: 'light' | 'subtle';
isLight?: boolean;
subscriptionUrl: string | null;
hideLink?: boolean;
deepLink?: string | null;
@@ -50,6 +51,7 @@ interface BlockButtonsProps {
export function BlockButtons({
buttons,
variant,
isLight,
subscriptionUrl,
hideLink,
deepLink,
@@ -80,8 +82,12 @@ export function BlockButtons({
const baseClass =
variant === 'light'
? 'rounded-xl border border-accent-500/40 px-4 py-2 text-sm font-medium text-accent-400 transition-all hover:bg-accent-500/10'
: 'rounded-xl px-3 py-1.5 text-sm font-medium text-dark-300 transition-all hover:bg-dark-700/50';
? isLight
? 'rounded-xl border border-accent-500/50 px-4 py-2 text-sm font-medium text-accent-600 shadow-sm transition-all hover:bg-accent-500/10'
: 'rounded-xl border border-accent-500/40 px-4 py-2 text-sm font-medium text-accent-400 transition-all hover:bg-accent-500/10'
: isLight
? 'rounded-xl px-3 py-1.5 text-sm font-medium text-dark-300 transition-all hover:bg-dark-700/30'
: 'rounded-xl px-3 py-1.5 text-sm font-medium text-dark-300 transition-all hover:bg-dark-700/50';
return (
<div className="mt-3 flex flex-wrap gap-2">
@@ -120,7 +126,7 @@ export function BlockButtons({
onClick={() => handleCopy(url)}
className={`flex items-center gap-2 ${
copied
? 'rounded-xl border border-success-500 bg-success-500/10 px-4 py-2 text-sm font-medium text-success-400'
? `rounded-xl border border-success-500 bg-success-500/10 px-4 py-2 text-sm font-medium ${isLight ? 'text-success-600' : 'text-success-400'}`
: baseClass
}`}
>

View File

@@ -5,6 +5,7 @@ import type { BlockRendererProps } from './types';
export function CardsBlock({
blocks,
isMobile,
isLight,
getLocalizedText,
getSvgHtml,
renderBlockButtons,
@@ -12,12 +13,16 @@ export function CardsBlock({
return (
<div className="space-y-3">
{blocks.map((block, index) => {
const gradientStyle = getColorGradient(block.svgIconColor || 'cyan');
const gradientStyle = getColorGradient(block.svgIconColor || 'cyan', isLight);
return (
<div
key={index}
className="rounded-2xl border border-dark-700/50 bg-dark-800/50 p-4 sm:p-5"
className={`rounded-2xl border p-4 sm:p-5 ${
isLight
? 'border-dark-700/60 bg-white/80 shadow-sm'
: 'border-dark-700/50 bg-dark-800/50'
}`}
>
<div className="flex items-start gap-3 sm:gap-4">
<ThemeIcon

View File

@@ -5,6 +5,7 @@ import type { BlockRendererProps } from './types';
export function MinimalBlock({
blocks,
isMobile,
isLight,
getLocalizedText,
getSvgHtml,
renderBlockButtons,
@@ -12,11 +13,18 @@ export function MinimalBlock({
return (
<div>
{blocks.map((block, index) => {
const gradientStyle = getColorGradient(block.svgIconColor || 'cyan');
const gradientStyle = getColorGradient(block.svgIconColor || 'cyan', isLight);
const isLast = index === blocks.length - 1;
return (
<div key={index} className={isLast ? 'pb-4' : 'mb-4 border-b border-dark-700/50 pb-4'}>
<div
key={index}
className={
isLast
? 'pb-4'
: `mb-4 border-b pb-4 ${isLight ? 'border-dark-700/40' : 'border-dark-700/50'}`
}
>
<div className="mb-2 flex items-center gap-3">
<ThemeIcon
getSvgHtml={getSvgHtml}

View File

@@ -5,6 +5,7 @@ import type { BlockRendererProps } from './types';
export function TimelineBlock({
blocks,
isMobile,
isLight,
getLocalizedText,
getSvgHtml,
renderBlockButtons,
@@ -12,7 +13,7 @@ export function TimelineBlock({
return (
<div className="space-y-0">
{blocks.map((block, index) => {
const gradientStyle = getColorGradientSolid(block.svgIconColor || 'cyan');
const gradientStyle = getColorGradientSolid(block.svgIconColor || 'cyan', isLight);
const isLast = index === blocks.length - 1;
return (
@@ -25,7 +26,9 @@ export function TimelineBlock({
gradientStyle={gradientStyle}
isMobile={isMobile}
/>
{!isLast && <div className="w-0.5 flex-1 bg-dark-700" />}
{!isLast && (
<div className={`w-0.5 flex-1 ${isLight ? 'bg-dark-700/40' : 'bg-dark-700'}`} />
)}
</div>
{/* Right column: content */}
<div className={`min-w-0 flex-1 ${isLast ? '' : 'pb-6'}`}>

View File

@@ -3,6 +3,7 @@ import type { RemnawaveBlockClient, RemnawaveButtonClient, LocalizedText } from
export interface BlockRendererProps {
blocks: RemnawaveBlockClient[];
isMobile: boolean;
isLight: boolean;
getLocalizedText: (text: LocalizedText | undefined) => string;
getSvgHtml: (key: string | undefined) => string;
renderBlockButtons: (

View File

@@ -1512,8 +1512,8 @@ input[type='checkbox']:hover:not(:checked) {
/* Light theme adjustments */
.light .wave-blob {
opacity: 0.6;
filter: blur(70px);
opacity: 0.35;
filter: blur(80px);
}
.light .wave-blob-1 {

View File

@@ -31,16 +31,37 @@ export interface ColorGradientStyle {
boxShadow?: string;
}
export const getColorGradient = (color: string): ColorGradientStyle => {
export const getColorGradient = (color: string, light?: boolean): ColorGradientStyle => {
const [r, g, b] = getRgb(color);
if (light) {
return {
background: `linear-gradient(135deg, rgba(${r},${g},${b},0.12) 0%, rgba(${r},${g},${b},0.05) 100%)`,
border: `1px solid rgba(${r},${g},${b},0.2)`,
boxShadow: `0 1px 3px rgba(${r},${g},${b},0.1)`,
};
}
return {
background: `linear-gradient(135deg, rgba(${r},${g},${b},0.15) 0%, rgba(${r},${g},${b},0.08) 100%)`,
border: `1px solid rgba(${r},${g},${b},0.3)`,
};
};
export const getColorGradientSolid = (color: string): ColorGradientStyle => {
export const getColorGradientSolid = (color: string, light?: boolean): ColorGradientStyle => {
const [r, g, b] = getRgb(color);
if (light) {
// Light theme: soft tinted background instead of dark solid
const light1 = [245 + r * 0.02, 243 + g * 0.02, 240 + b * 0.02].map((v) =>
Math.min(255, Math.floor(v)),
);
const light2 = [240 + r * 0.03, 237 + g * 0.03, 233 + b * 0.03].map((v) =>
Math.min(255, Math.floor(v)),
);
return {
background: `linear-gradient(135deg, rgb(${light1}) 0%, rgb(${light2}) 100%)`,
border: `1px solid rgba(${r},${g},${b},0.25)`,
boxShadow: `0 1px 4px rgba(${r},${g},${b},0.12)`,
};
}
const dark1 = [22 + r * 0.08, 27 + g * 0.08, 35 + b * 0.08].map(Math.floor);
const dark2 = [20 + r * 0.05, 24 + g * 0.05, 30 + b * 0.05].map(Math.floor);