feat: select ‘25 banner styled (#38803)

* basic lines

* horizontal lines

* stop at right

* misc touches

* polish

* polish

* fix: dark mode

* clean up markup

* revert LW banner logic and props

These are now unused, so I'm reverting to how they were. That will make the next LW a bit easier.

* fix: type assertion

TypeScript not recognizing CSS custom properties. Now accessing the React.CSSProperties type

* nit: date format

Make more US-friendly
This commit is contained in:
Danny White
2025-09-19 10:07:16 +10:00
committed by GitHub
parent 0674343912
commit 10da72a8ec
5 changed files with 87 additions and 30 deletions

View File

@@ -53,11 +53,11 @@ export function LW15Banner() {
<div className="relative z-10 flex items-center justify-center">
<div className="w-full flex gap-5 md:gap-10 items-center md:justify-center text-sm">
<p className="flex gap-1.5 items-center font-mono uppercase tracking-widest text-sm">
{announcement.title}
{announcement.text}
</p>
<p className="text-sm hidden sm:block">{announcement.desc}</p>
<p className="text-sm hidden sm:block">{announcement.launch}</p>
<Button size="tiny" type="default" className="px-2 !leading-none text-xs" asChild>
<Link href={announcement.link}>{announcement.button}</Link>
<Link href={announcement.link}>{announcement.cta}</Link>
</Button>
</div>
</div>

View File

@@ -1,26 +1,84 @@
import React from 'react'
import Link from 'next/link'
import { Button } from 'ui/src/components/Button'
import announcement from './data.json'
export function SelectBanner() {
const selectSiteUrl = 'https://select.supabase.com/'
const desc = ['Our first user conference', 'Oct 3 2025', 'Guillermo Rauch, Dylan Field, and more']
const cta = 'Save your seat'
const baseStyles = 'flex flex-col justify-center border-l border-muted py-8 '
const textBlockStyles =
baseStyles +
'pr-8 text-xs font-mono uppercase leading-none tracking-wide text-white/50 [&_p]:mt-[5px]'
return (
<div className="relative w-full p-4 flex items-center group justify-center text-foreground-contrast dark:text-white bg-black border-b border-muted transition-colors overflow-hidden ">
<div className="relative z-10 flex items-center justify-center">
<div className="w-full flex gap-5 md:gap-10 items-center md:justify-center text-sm">
<Link target={announcement.target ?? '_self'} href={announcement.link}>
<img src="/images/select/supabase-select.svg" alt="Supabase Select" className="w-32" />
<div
className="dark relative w-full flex items-center group justify-center text-foreground-contrast bg-black border-b border-muted transition-colors overflow-hidden"
style={
{
'--line-color': 'hsl(var(--border-muted))',
'--line-width': '1px',
'--offset-from-top': '64px',
'--line-spacing': '12px', // Match -3 utility spacing used elsewhere
backgroundImage: `
/* Top horizontal line: offset from middle line by line spacing */
linear-gradient(to bottom, transparent 0, transparent calc(var(--offset-from-top) - var(--line-spacing)), var(--line-color) calc(var(--offset-from-top) - var(--line-spacing)), var(--line-color) calc(var(--offset-from-top) - var(--line-spacing) + var(--line-width)), transparent calc(var(--offset-from-top) - var(--line-spacing) + var(--line-width))),
/* Middle horizontal line */
linear-gradient(to bottom, transparent 0, transparent var(--offset-from-top), var(--line-color) var(--offset-from-top), var(--line-color) calc(var(--offset-from-top) + var(--line-width)), transparent calc(var(--offset-from-top) + var(--line-width))),
/* Bottom horizontal line: offset from middle line by line spacing */
linear-gradient(to bottom, transparent 0, transparent calc(var(--offset-from-top) + var(--line-spacing)), var(--line-color) calc(var(--offset-from-top) + var(--line-spacing)), var(--line-color) calc(var(--offset-from-top) + var(--line-spacing) + var(--line-width)), transparent calc(var(--offset-from-top) + var(--line-spacing) + var(--line-width)))
`,
backgroundSize: '100% 100%',
backgroundRepeat: 'no-repeat',
} as React.CSSProperties
}
>
<div className="relative z-10 flex gap-5 items-stretch justify-center px-3 border-l border-muted">
<div className={`${baseStyles} -px-3`}>
<Link
target="_blank"
href={selectSiteUrl}
className="transition-opacity hover:opacity-80"
>
<img src="/images/select/supabase-select.svg" alt="Supabase Select" className="w-36" />
</Link>
</div>
<div className={`${textBlockStyles} hidden md:flex`}>
<p>{desc[0]}</p>
</div>
<div className={`${textBlockStyles} hidden sm:flex`}>
<p>{desc[1]}</p>
</div>
<div className={`${textBlockStyles} hidden xl:flex`}>
<p>{desc[2]}</p>
</div>
<p className="text-sm hidden sm:block">
{announcement.desc}
<span className="hidden xl:inline">{announcement.verbose}</span>
</p>
<Button size="tiny" type="default" className="px-2 !leading-none text-xs" asChild>
<Link target={announcement.target ?? '_self'} href={announcement.link}>
{announcement.button}
</Link>
</Button>
<div className="flex flex-col justify-center -px-4 border-x border-muted border-dashed relative after:absolute after:top-0 after:left-full after:w-screen after:h-full after:bg-black after:-z-10">
<Link
target="_blank"
href={selectSiteUrl}
className="relative before:absolute before:inset-0 before:bg-black before:-z-10 px-4 py-1 h-10 flex items-center justify-center bg-brand-600/20 hover:bg-brand-600/50 outline-1 outline-dashed outline-brand-600/40 hover:outline-brand-600 text-sm text-white font-medium transition-all duration-200"
>
{cta}
{/* Crosshairs */}
<div className="absolute pointer-events-none inset-0 z-10">
{['top-left', 'top-right', 'bottom-left', 'bottom-right'].map((position) => (
<div
key={position}
className={`absolute ${position === 'top-left' ? 'top-0 left-0' : position === 'top-right' ? 'top-0 right-0' : position === 'bottom-left' ? 'bottom-0 left-0' : 'bottom-0 right-0'}`}
>
<div
className={`absolute ${position.includes('left') ? '-left-px' : '-right-px'} ${position.includes('top') ? '-top-[3px]' : '-bottom-[3px]'} w-px h-[5px]`}
style={{ backgroundColor: 'hsl(var(--brand-600))' }}
/>
<div
className={`absolute ${position.includes('left') ? '-left-[3px]' : '-right-[3px]'} ${position.includes('top') ? '-top-px' : '-bottom-px'} w-[5px] h-px`}
style={{ backgroundColor: 'hsl(var(--brand-600))' }}
/>
</div>
))}
</div>
</Link>
</div>
</div>
</div>

View File

@@ -1,8 +1,7 @@
{
"title": "Supabase Select",
"desc": "Our first user conference · October 3, San Francisco",
"verbose": " · Guillermo Rauch, Dylan Field, and more",
"link": "https://select.supabase.com/",
"target": "_blank",
"button": "Save your seat"
"text": "",
"launch": "",
"launchDate": "2025-07-17T08:00:00.000-07:00",
"link": "#",
"cta": "Learn more"
}

View File

@@ -46,7 +46,7 @@ const PromoToast = () => {
poster={`${process.env.NEXT_PUBLIC_SUPABASE_URL}/storage/v1/object/public/images/launch-week/lw15/assets/lw15-galaxy.png`}
/>
<div className="relative z-10 text-foreground-lighter uppercase flex flex-col text-sm w-full mb-2">
<span className="mb-1">{announcement.title}</span>
<span className="mb-1">{announcement.text}</span>
<p className="relative z-10 text-foreground flex flex-col text-xl w-full leading-7"></p>
</div>

View File

@@ -9,8 +9,8 @@ import { X } from 'lucide-react'
export interface AnnouncementProps {
show: boolean
title: string
launchDate: string | null
text: string
launchDate: string
link: string
badge?: string
}
@@ -66,7 +66,7 @@ const Announcement = ({
<div className={cn('relative z-40 w-full', className)}>
{dismissable && !isLaunchWeekSection && (
<div
className="absolute z-50 right-4 flex h-full items-center opacity-100 text-foreground-contrast dark:text-foreground transition-opacity hover:opacity-100 hover:cursor-pointer"
className="absolute z-50 right-4 flex h-full items-center opacity-100 text-foreground-contrast dark:text-foreground transition-opacity hover:opacity-80 hover:cursor-pointer"
onClick={handleClose}
>
<X size={16} />