mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-30 02:23:47 +00:00
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:
@@ -8,6 +8,7 @@ import type {
|
|||||||
RemnawavePlatformData,
|
RemnawavePlatformData,
|
||||||
RemnawaveButtonClient,
|
RemnawaveButtonClient,
|
||||||
} from '@/types';
|
} from '@/types';
|
||||||
|
import { useTheme } from '@/hooks/useTheme';
|
||||||
import { CardsBlock, TimelineBlock, AccordionBlock, MinimalBlock, BlockButtons } from './blocks';
|
import { CardsBlock, TimelineBlock, AccordionBlock, MinimalBlock, BlockButtons } from './blocks';
|
||||||
import type { BlockRendererProps } from './blocks';
|
import type { BlockRendererProps } from './blocks';
|
||||||
|
|
||||||
@@ -51,6 +52,7 @@ export default function InstallationGuide({
|
|||||||
onGoBack,
|
onGoBack,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
|
const { isLight } = useTheme();
|
||||||
|
|
||||||
const detectedPlatform = useMemo(() => detectPlatform(), []);
|
const detectedPlatform = useMemo(() => detectPlatform(), []);
|
||||||
const isMobile = typeof window !== 'undefined' && window.innerWidth < 768;
|
const isMobile = typeof window !== 'undefined' && window.innerWidth < 768;
|
||||||
@@ -127,6 +129,7 @@ export default function InstallationGuide({
|
|||||||
<BlockButtons
|
<BlockButtons
|
||||||
buttons={buttons}
|
buttons={buttons}
|
||||||
variant={variant}
|
variant={variant}
|
||||||
|
isLight={isLight}
|
||||||
subscriptionUrl={appConfig.subscriptionUrl}
|
subscriptionUrl={appConfig.subscriptionUrl}
|
||||||
hideLink={appConfig.hideLink}
|
hideLink={appConfig.hideLink}
|
||||||
deepLink={selectedApp?.deepLink}
|
deepLink={selectedApp?.deepLink}
|
||||||
@@ -140,6 +143,7 @@ export default function InstallationGuide({
|
|||||||
appConfig.subscriptionUrl,
|
appConfig.subscriptionUrl,
|
||||||
appConfig.hideLink,
|
appConfig.hideLink,
|
||||||
selectedApp?.deepLink,
|
selectedApp?.deepLink,
|
||||||
|
isLight,
|
||||||
getLocalizedText,
|
getLocalizedText,
|
||||||
getBaseTranslation,
|
getBaseTranslation,
|
||||||
getSvgHtml,
|
getSvgHtml,
|
||||||
@@ -221,9 +225,11 @@ export default function InstallationGuide({
|
|||||||
if (app) setSelectedApp(app);
|
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 ${
|
className={`appearance-none rounded-xl border py-2 pr-8 text-sm font-medium outline-none transition-colors ${
|
||||||
currentPlatformSvg ? 'pl-10' : 'pl-4'
|
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) => (
|
{availablePlatforms.map((p) => (
|
||||||
<option key={p} value={p}>
|
<option key={p} value={p}>
|
||||||
@@ -258,7 +264,11 @@ export default function InstallationGuide({
|
|||||||
onClick={() => setSelectedApp(app)}
|
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] ${
|
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
|
isSelected
|
||||||
? 'bg-accent-500/15 text-accent-400 ring-1 ring-accent-500/40'
|
? 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'
|
: 'border border-dark-700/50 bg-dark-800/80 text-dark-200 hover:border-dark-600/50 hover:bg-dark-700/80'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -306,6 +316,7 @@ export default function InstallationGuide({
|
|||||||
<Renderer
|
<Renderer
|
||||||
blocks={selectedApp.blocks}
|
blocks={selectedApp.blocks}
|
||||||
isMobile={isMobile}
|
isMobile={isMobile}
|
||||||
|
isLight={isLight}
|
||||||
getLocalizedText={getLocalizedText}
|
getLocalizedText={getLocalizedText}
|
||||||
getSvgHtml={getSvgHtml}
|
getSvgHtml={getSvgHtml}
|
||||||
renderBlockButtons={renderBlockButtons}
|
renderBlockButtons={renderBlockButtons}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type { BlockRendererProps } from './types';
|
|||||||
export function AccordionBlock({
|
export function AccordionBlock({
|
||||||
blocks,
|
blocks,
|
||||||
isMobile,
|
isMobile,
|
||||||
|
isLight,
|
||||||
getLocalizedText,
|
getLocalizedText,
|
||||||
getSvgHtml,
|
getSvgHtml,
|
||||||
renderBlockButtons,
|
renderBlockButtons,
|
||||||
@@ -15,14 +16,20 @@ export function AccordionBlock({
|
|||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{blocks.map((block, index) => {
|
{blocks.map((block, index) => {
|
||||||
const gradientStyle = getColorGradient(block.svgIconColor || 'cyan');
|
const gradientStyle = getColorGradient(block.svgIconColor || 'cyan', isLight);
|
||||||
const isOpen = openIndex === index;
|
const isOpen = openIndex === index;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className={`overflow-hidden rounded-2xl border bg-dark-800/50 transition-colors ${
|
className={`overflow-hidden rounded-2xl border transition-colors ${
|
||||||
isOpen ? 'border-accent-500/30' : 'border-dark-700/50'
|
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 */}
|
{/* Control */}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ const CheckIcon = () => (
|
|||||||
interface BlockButtonsProps {
|
interface BlockButtonsProps {
|
||||||
buttons: RemnawaveButtonClient[] | undefined;
|
buttons: RemnawaveButtonClient[] | undefined;
|
||||||
variant: 'light' | 'subtle';
|
variant: 'light' | 'subtle';
|
||||||
|
isLight?: boolean;
|
||||||
subscriptionUrl: string | null;
|
subscriptionUrl: string | null;
|
||||||
hideLink?: boolean;
|
hideLink?: boolean;
|
||||||
deepLink?: string | null;
|
deepLink?: string | null;
|
||||||
@@ -50,6 +51,7 @@ interface BlockButtonsProps {
|
|||||||
export function BlockButtons({
|
export function BlockButtons({
|
||||||
buttons,
|
buttons,
|
||||||
variant,
|
variant,
|
||||||
|
isLight,
|
||||||
subscriptionUrl,
|
subscriptionUrl,
|
||||||
hideLink,
|
hideLink,
|
||||||
deepLink,
|
deepLink,
|
||||||
@@ -80,7 +82,11 @@ export function BlockButtons({
|
|||||||
|
|
||||||
const baseClass =
|
const baseClass =
|
||||||
variant === 'light'
|
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'
|
? 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';
|
: 'rounded-xl px-3 py-1.5 text-sm font-medium text-dark-300 transition-all hover:bg-dark-700/50';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -120,7 +126,7 @@ export function BlockButtons({
|
|||||||
onClick={() => handleCopy(url)}
|
onClick={() => handleCopy(url)}
|
||||||
className={`flex items-center gap-2 ${
|
className={`flex items-center gap-2 ${
|
||||||
copied
|
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
|
: baseClass
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type { BlockRendererProps } from './types';
|
|||||||
export function CardsBlock({
|
export function CardsBlock({
|
||||||
blocks,
|
blocks,
|
||||||
isMobile,
|
isMobile,
|
||||||
|
isLight,
|
||||||
getLocalizedText,
|
getLocalizedText,
|
||||||
getSvgHtml,
|
getSvgHtml,
|
||||||
renderBlockButtons,
|
renderBlockButtons,
|
||||||
@@ -12,12 +13,16 @@ export function CardsBlock({
|
|||||||
return (
|
return (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{blocks.map((block, index) => {
|
{blocks.map((block, index) => {
|
||||||
const gradientStyle = getColorGradient(block.svgIconColor || 'cyan');
|
const gradientStyle = getColorGradient(block.svgIconColor || 'cyan', isLight);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={index}
|
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">
|
<div className="flex items-start gap-3 sm:gap-4">
|
||||||
<ThemeIcon
|
<ThemeIcon
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type { BlockRendererProps } from './types';
|
|||||||
export function MinimalBlock({
|
export function MinimalBlock({
|
||||||
blocks,
|
blocks,
|
||||||
isMobile,
|
isMobile,
|
||||||
|
isLight,
|
||||||
getLocalizedText,
|
getLocalizedText,
|
||||||
getSvgHtml,
|
getSvgHtml,
|
||||||
renderBlockButtons,
|
renderBlockButtons,
|
||||||
@@ -12,11 +13,18 @@ export function MinimalBlock({
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{blocks.map((block, index) => {
|
{blocks.map((block, index) => {
|
||||||
const gradientStyle = getColorGradient(block.svgIconColor || 'cyan');
|
const gradientStyle = getColorGradient(block.svgIconColor || 'cyan', isLight);
|
||||||
const isLast = index === blocks.length - 1;
|
const isLast = index === blocks.length - 1;
|
||||||
|
|
||||||
return (
|
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">
|
<div className="mb-2 flex items-center gap-3">
|
||||||
<ThemeIcon
|
<ThemeIcon
|
||||||
getSvgHtml={getSvgHtml}
|
getSvgHtml={getSvgHtml}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type { BlockRendererProps } from './types';
|
|||||||
export function TimelineBlock({
|
export function TimelineBlock({
|
||||||
blocks,
|
blocks,
|
||||||
isMobile,
|
isMobile,
|
||||||
|
isLight,
|
||||||
getLocalizedText,
|
getLocalizedText,
|
||||||
getSvgHtml,
|
getSvgHtml,
|
||||||
renderBlockButtons,
|
renderBlockButtons,
|
||||||
@@ -12,7 +13,7 @@ export function TimelineBlock({
|
|||||||
return (
|
return (
|
||||||
<div className="space-y-0">
|
<div className="space-y-0">
|
||||||
{blocks.map((block, index) => {
|
{blocks.map((block, index) => {
|
||||||
const gradientStyle = getColorGradientSolid(block.svgIconColor || 'cyan');
|
const gradientStyle = getColorGradientSolid(block.svgIconColor || 'cyan', isLight);
|
||||||
const isLast = index === blocks.length - 1;
|
const isLast = index === blocks.length - 1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -25,7 +26,9 @@ export function TimelineBlock({
|
|||||||
gradientStyle={gradientStyle}
|
gradientStyle={gradientStyle}
|
||||||
isMobile={isMobile}
|
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>
|
</div>
|
||||||
{/* Right column: content */}
|
{/* Right column: content */}
|
||||||
<div className={`min-w-0 flex-1 ${isLast ? '' : 'pb-6'}`}>
|
<div className={`min-w-0 flex-1 ${isLast ? '' : 'pb-6'}`}>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { RemnawaveBlockClient, RemnawaveButtonClient, LocalizedText } from
|
|||||||
export interface BlockRendererProps {
|
export interface BlockRendererProps {
|
||||||
blocks: RemnawaveBlockClient[];
|
blocks: RemnawaveBlockClient[];
|
||||||
isMobile: boolean;
|
isMobile: boolean;
|
||||||
|
isLight: boolean;
|
||||||
getLocalizedText: (text: LocalizedText | undefined) => string;
|
getLocalizedText: (text: LocalizedText | undefined) => string;
|
||||||
getSvgHtml: (key: string | undefined) => string;
|
getSvgHtml: (key: string | undefined) => string;
|
||||||
renderBlockButtons: (
|
renderBlockButtons: (
|
||||||
|
|||||||
@@ -1512,8 +1512,8 @@ input[type='checkbox']:hover:not(:checked) {
|
|||||||
|
|
||||||
/* Light theme adjustments */
|
/* Light theme adjustments */
|
||||||
.light .wave-blob {
|
.light .wave-blob {
|
||||||
opacity: 0.6;
|
opacity: 0.35;
|
||||||
filter: blur(70px);
|
filter: blur(80px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.light .wave-blob-1 {
|
.light .wave-blob-1 {
|
||||||
|
|||||||
@@ -31,16 +31,37 @@ export interface ColorGradientStyle {
|
|||||||
boxShadow?: string;
|
boxShadow?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getColorGradient = (color: string): ColorGradientStyle => {
|
export const getColorGradient = (color: string, light?: boolean): ColorGradientStyle => {
|
||||||
const [r, g, b] = getRgb(color);
|
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 {
|
return {
|
||||||
background: `linear-gradient(135deg, rgba(${r},${g},${b},0.15) 0%, rgba(${r},${g},${b},0.08) 100%)`,
|
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)`,
|
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);
|
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 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);
|
const dark2 = [20 + r * 0.05, 24 + g * 0.05, 30 + b * 0.05].map(Math.floor);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user