feat: FDButton component with dark/light hover, fix yellow button on navy

This commit is contained in:
Jeffrey 2026-02-19 20:45:11 +01:00
parent 80be2c4098
commit f1462cf7c3
6 changed files with 71 additions and 82 deletions

2
next-env.d.ts vendored
View File

@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/types/routes.d.ts";
import "./.next/dev/types/routes.d.ts";
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

View File

@ -453,6 +453,35 @@ html[data-theme='light'] {
box-shadow: 0 4px 12px -2px rgba(254, 204, 2, 0.25);
}
/* Same as fd-btn-primary but hovers to WHITE use on navy/dark backgrounds.
Applied automatically via <FDButton variant="primary" onDark={true}> */
.fd-btn-primary-dark {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.75rem 2rem;
background-color: var(--color-fd-yellow);
color: var(--color-fd-navy);
font-family: var(--font-joey-bold);
font-size: var(--text-fd-btn);
line-height: var(--text-fd-btn--line-height);
border-radius: 9999px;
text-decoration: none;
cursor: pointer;
border: 2px solid transparent;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.fd-btn-primary-dark:hover {
background-color: #ffffff;
color: var(--color-fd-navy);
box-shadow: 0 8px 24px -4px rgba(14, 35, 56, 0.3);
transform: translateY(-2px);
}
.fd-btn-primary-dark:active {
transform: translateY(0);
box-shadow: 0 4px 12px -2px rgba(255, 255, 255, 0.15);
}
.fd-btn-secondary {
display: inline-flex;
align-items: center;
@ -628,6 +657,7 @@ html[data-theme='light'] {
@media (max-width: 47.9375rem) {
/* Full-width buttons on mobile for clear tap targets */
.fd-btn-primary,
.fd-btn-primary-dark,
.fd-btn-secondary,
.fd-btn-secondary-dark,
.fd-btn-navy,

View File

@ -1,5 +1,7 @@
import React from 'react'
import type { Metadata } from 'next'
import { FDButton } from '@/components/FDButton'
export const metadata: Metadata = {
title: '404 Sidan hittades inte | Fiber Direkt',
@ -41,9 +43,9 @@ export default function NotFound() {
{/* CTA */}
<div className="mt-2">
<a href="/" className="fd-btn-primary">
<FDButton href="/" variant="primary" onDark={false}>
Tillbaka till startsidan
</a>
</FDButton>
</div>
</div>

View File

@ -5,34 +5,30 @@ export const FDCtaSideImageBlock: Block = {
interfaceName: 'FDCtaSideImageBlock',
labels: {
singular: 'FD CTA med bild',
plural: 'FD CTA med bild',
plural: 'FD CTA med bilder',
},
fields: [
{
name: 'heading',
type: 'text',
localized: true,
required: true,
label: 'Rubrik',
},
{
name: 'body',
type: 'textarea',
localized: true,
required: true,
label: 'Brödtext',
},
{
name: 'ctaText',
type: 'text',
localized: true,
label: 'CTA-knapp text',
defaultValue: 'Läs mer',
},
{
name: 'ctaLink',
type: 'text',
localized: true,
label: 'CTA-knapp länk',
defaultValue: '#',
},
@ -40,8 +36,7 @@ export const FDCtaSideImageBlock: Block = {
name: 'image',
type: 'upload',
relationTo: 'media',
required: true,
label: 'Bild',
label: 'Bild (valfri)',
},
{
name: 'imagePosition',
@ -61,58 +56,6 @@ export const FDCtaSideImageBlock: Block = {
options: [
{ label: 'Ljust', value: 'light' },
{ label: 'Mörkt', value: 'dark' },
{ label: 'Anpassad färg', value: 'custom' },
],
},
{
name: 'customBackgroundColor',
type: 'text',
label: 'Anpassad bakgrundsfärg',
admin: {
condition: (_, siblingData) => siblingData?.theme === 'custom',
description: 'Valfri HEX-färg, t.ex. #1a3a5c eller #fecc02',
},
},
{
name: 'customTextLight',
type: 'checkbox',
label: 'Ljus text (för mörka bakgrunder)',
defaultValue: true,
admin: {
condition: (_, siblingData) => siblingData?.theme === 'custom',
description: 'Aktivera för vit text på mörk anpassad bakgrund',
},
},
{
name: 'imageOverlay',
type: 'select',
label: 'Bild-overlay',
defaultValue: 'none',
admin: {
description: 'Tonad overlay över bilden med varumärkesfärg',
},
options: [
{ label: 'Ingen', value: 'none' },
{ label: 'Navy', value: 'navy' },
{ label: 'Gul', value: 'yellow' },
{ label: 'Svart', value: 'black' },
],
},
{
name: 'imageOverlayOpacity',
type: 'select',
label: 'Overlay-styrka',
defaultValue: '30',
admin: {
condition: (_, siblingData) =>
Boolean(siblingData?.imageOverlay) && siblingData?.imageOverlay !== 'none',
description: 'Hur stark overlay över bilden',
},
options: [
{ label: 'Lätt (20%)', value: '20' },
{ label: 'Medium (30%)', value: '30' },
{ label: 'Stark (50%)', value: '50' },
{ label: 'Mycket stark (70%)', value: '70' },
],
},
],

View File

@ -1,42 +1,56 @@
import React from 'react'
type AnchorProps = React.AnchorHTMLAttributes<HTMLAnchorElement>
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement>
type FDButtonProps = {
href: string
children: React.ReactNode
variant?: 'primary' | 'outline'
onDark?: boolean
className?: string
} & (
| ({ as?: 'a' } & AnchorProps)
| ({ as: 'button' } & ButtonProps)
)
const classMap = {
'primary-light': 'fd-btn-primary',
'primary-dark': 'fd-btn-primary-dark',
'outline-light': 'fd-btn-secondary',
'outline-dark': 'fd-btn-secondary-dark',
}
/**
* FDButton shared button component for all FD blocks.
* FDButton single button component for all FD blocks.
*
* variant="primary" onDark={true} yellow bg, hover white (use on navy backgrounds)
* variant="primary" onDark={false} yellow bg, hover yellow/80 (use on light backgrounds)
* variant="outline" onDark={true} white border + text, hover white/10
* variant="outline" onDark={false} navy border + text, hover navy/5
* Renders as <a> by default, or <button> when as="button" (e.g. forms, newsletter).
*
* variant="primary" onDark={false} fd-btn-primary yellow navy hover (light backgrounds)
* variant="primary" onDark={true} fd-btn-primary-dark yellow white hover (navy backgrounds)
* variant="outline" onDark={false} fd-btn-secondary outline on light
* variant="outline" onDark={true} fd-btn-secondary-dark outline on dark
*/
export const FDButton: React.FC<FDButtonProps> = ({
href,
export const FDButton = ({
children,
variant = 'primary',
onDark = false,
className = '',
}) => {
const base =
'inline-flex items-center justify-center px-8 py-2.5 rounded-full font-joey-bold text-lg md:text-2xl leading-[38px] transition-colors'
as: Tag = 'a',
...rest
}: FDButtonProps) => {
const key = `${variant}-${onDark ? 'dark' : 'light'}` as keyof typeof classMap
const cls = `${classMap[key]} ${className}`.trim()
const styles = {
'primary-dark': 'bg-fd-yellow text-fd-navy hover:bg-white hover:text-fd-navy',
'primary-light': 'bg-fd-yellow text-fd-navy hover:bg-fd-yellow/80',
'outline-dark': 'border-2 border-white text-white hover:bg-white/10',
'outline-light': 'border-2 border-fd-navy text-fd-navy hover:bg-fd-navy/5',
if (Tag === 'button') {
return (
<button className={cls} {...(rest as ButtonProps)}>
{children}
</button>
)
}
const key = `${variant}-${onDark ? 'dark' : 'light'}` as keyof typeof styles
return (
<a href={href} className={`${base} ${styles[key]} ${className}`}>
<a className={cls} {...(rest as AnchorProps)}>
{children}
</a>
)

File diff suppressed because one or more lines are too long