Style Consistency Changes and Element Improvements (#541)

Co-authored-by: braginini <bangvalo@gmail.com>
This commit is contained in:
Brandon Hopkins
2026-01-11 00:27:56 -08:00
committed by GitHub
parent aef978f63d
commit becf4bd6e4
30 changed files with 645 additions and 283 deletions

View File

@@ -1,49 +0,0 @@
import { Button } from '@/components/Button'
import { Heading } from '@/components/Heading'
const aboutNetbird = [
{
href: '/about-netbird/how-netbird-works',
name: 'How NetBird works',
description: 'Concepts, architecture, protocols, and more.',
},
{
href: '/about-netbird/netbird-vs-traditional-vpn',
name: 'NetBird vs. traditional VPN',
description:
'Learn how NetBird compares to traditional VPNs and why it is better.',
},
{
href: '/about-netbird/why-wireguard-with-netbird',
name: 'Why WireGuard with NetBird',
description:
'Learn why and how NetBird uses WireGuard.',
},
]
export function AboutNetbird() {
return (
<div className="my-16 xl:max-w-none">
<Heading level={2} id="howNetbirdWorks">
About NetBird
</Heading>
<div className="not-prose mt-4 grid grid-cols-1 gap-8 border-t border-zinc-900/5 pt-10 dark:border-white/5 sm:grid-cols-2 xl:grid-cols-4">
{aboutNetbird.map((guide) => (
<div key={guide.href}>
<h3 className="text-sm font-semibold text-zinc-900 dark:text-white">
{guide.name}
</h3>
<p className="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
{guide.description}
</p>
<p className="mt-4">
<Button href={guide.href} variant="text" arrow="right">
Read more
</Button>
</p>
</div>
))}
</div>
</div>
)
}

View File

@@ -16,14 +16,14 @@ function ArrowIcon(props) {
const variantStyles = {
primary:
'rounded-full bg-zinc-900 py-1 px-3 text-white hover:bg-zinc-700 dark:bg-orange-400/10 dark:text-orange-400 dark:ring-1 dark:ring-inset dark:ring-orange-400/20 dark:hover:bg-orange-400/10 dark:hover:text-orange-300 dark:hover:ring-orange-300',
'rounded-[5px] bg-netbird text-white border-0 border-transparent duration-300 relative overflow-hidden group',
secondary:
'rounded-full bg-zinc-100 py-1 px-3 text-zinc-900 hover:bg-zinc-200 dark:bg-zinc-800/40 dark:text-zinc-400 dark:ring-1 dark:ring-inset dark:ring-zinc-800 dark:hover:bg-zinc-800 dark:hover:text-zinc-300',
filled:
'rounded-full bg-zinc-900 py-1 px-3 text-white hover:bg-zinc-700 dark:bg-orange-500 dark:text-white dark:hover:bg-orange-400',
'rounded-full bg-zinc-900 py-1 px-3 text-white hover:bg-zinc-700 dark:bg-netbird dark:text-white dark:hover:bg-netbird-dark',
outline:
'rounded-full py-1 px-3 text-zinc-700 ring-1 ring-inset ring-zinc-900/10 hover:bg-zinc-900/2.5 hover:text-zinc-900 dark:text-zinc-400 dark:ring-white/10 dark:hover:bg-white/5 dark:hover:text-white',
text: 'text-orange-500 hover:text-orange-600 dark:text-orange-400 dark:hover:text-orange-500',
text: 'text-netbird hover:text-netbird-dark dark:text-netbird dark:hover:text-netbird-light',
}
export function Button({
@@ -36,7 +36,7 @@ export function Button({
let Component = props.href ? Link : 'button'
className = clsx(
'inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition whitespace-nowrap',
'inline-flex gap-0.5 justify-center text-[12px] md:text-xs px-3 py-2 md:px-3 md:py-2 lg:px-4 lg:py-2.5 font-medium transition whitespace-nowrap items-center',
variantStyles[variant],
className
)
@@ -52,6 +52,22 @@ export function Button({
/>
)
if (variant === 'primary') {
return (
<div className="relative inline-flex group transition-all" onClick={props.href ? undefined : props.onClick}>
<span className="absolute h-full w-full left-0 top-0 blur-sm bg-netbird z-0 transition-all duration-200 transform-gpu opacity-0 group-hover:opacity-100 pointer-events-none"></span>
<Component className={className} {...props}>
<span className="absolute h-full w-full left-0 top-0 z-10 bg-gradient-to-br from-netbird to-netbird-dark transition-all duration-200 transform-gpu opacity-0 group-hover:opacity-100 pointer-events-none"></span>
<span className="z-20 relative flex gap-2 items-center transition-all">
{arrow === 'left' && arrowIcon}
{children}
{arrow === 'right' && arrowIcon}
</span>
</Component>
</div>
)
}
return (
<Component className={className} {...props}>
{arrow === 'left' && arrowIcon}

View File

@@ -1,7 +1,7 @@
import { forwardRef } from 'react'
import Link from 'next/link'
import clsx from 'clsx'
import { motion, useScroll, useTransform } from 'framer-motion'
import { motion } from 'framer-motion'
import { Button } from '@/components/Button'
import { Logo } from '@/components/Logo'
@@ -16,10 +16,10 @@ import { useAnnouncements } from '@/components/announcement-banner/AnnouncementB
function TopLevelNavItem({ href, children }) {
return (
<li>
<li className="block text-[12px] lg:text-[13.5px] m-0 p-0 leading-none">
<Link
href={href}
className="text-sm leading-5 text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white"
className="px-2 lg:px-3 py-1 lg:py-2 opacity-60 hover:opacity-100 hover:bg-zinc-900/5 dark:hover:bg-neutral-900/60 hover:border-zinc-900/10 dark:hover:border-neutral-800 border border-transparent rounded-md leading-none transition-all duration-200 text-zinc-900 dark:text-white inline-flex items-center"
>
{children}
</Link>
@@ -32,45 +32,37 @@ export const Header = forwardRef(function Header({ className }, ref) {
let isInsideMobileNavigation = useIsInsideMobileNavigation()
let { bannerHeight } = useAnnouncements()
let { scrollY } = useScroll()
let bgOpacityLight = useTransform(scrollY, [0, 72], [0.5, 0.9])
let bgOpacityDark = useTransform(scrollY, [0, 72], [0.2, 0.8])
return (
<motion.div
ref={ref}
className={clsx(
className,
'fixed inset-x-0 top-0 z-40 flex h-14 items-center justify-between gap-12 px-4 transition sm:px-6 lg:left-72 lg:z-30 lg:px-8 xl:left-80',
!isInsideMobileNavigation &&
'backdrop-blur-sm dark:backdrop-blur lg:left-72 xl:left-80',
isInsideMobileNavigation
? 'bg-white dark:bg-zinc-900'
: 'bg-white/[var(--bg-opacity-light)] dark:bg-zinc-900/[var(--bg-opacity-dark)]'
)}
className={clsx(
className,
'fixed inset-x-0 top-0 z-40 flex items-center justify-between gap-3 px-5 transition h-[64px] lg:left-72 lg:z-50 lg:px-8 xl:left-80 min-h-[64px] lg:pointer-events-auto',
!isInsideMobileNavigation &&
'backdrop-blur-lg bg-white/70 dark:bg-[#181A1D]/95 lg:left-72 xl:left-80',
isInsideMobileNavigation &&
'bg-white/70 dark:bg-[#181A1D]/95 backdrop-blur-lg'
)}
style={{
'--bg-opacity-light': bgOpacityLight,
'--bg-opacity-dark': bgOpacityDark,
top: bannerHeight,
}}
>
<div
className={clsx(
'absolute inset-x-0 top-full h-px transition',
(isInsideMobileNavigation || !mobileNavIsOpen) &&
'bg-zinc-900/7.5 dark:bg-white/7.5'
'absolute inset-x-0 top-full h-px transition border-b border-transparent',
!isInsideMobileNavigation && 'border-zinc-900/10 dark:border-neutral-700/50'
)}
/>
<Search />
<div className="flex items-center gap-5 lg:hidden">
<div className="flex items-center gap-2 lg:hidden">
<MobileNavigation />
<Link href="/" aria-label="Home">
<Logo className="h-6" />
</Link>
</div>
<div className="flex items-center gap-5">
<div className="flex items-center gap-3 xl:gap-2">
<nav className="hidden md:block">
<ul role="list" className="flex items-center gap-8">
<ul role="list" className="flex items-center gap-3 xl:gap-2 m-0 p-0 list-none">
<TopLevelNavItem href="https://netbird.io/">Home</TopLevelNavItem>
<TopLevelNavItem href="/">Docs</TopLevelNavItem>
<TopLevelNavItem href="/api">API</TopLevelNavItem>
@@ -79,8 +71,8 @@ export const Header = forwardRef(function Header({ className }, ref) {
<TopLevelNavItem href="/slack-url">Support</TopLevelNavItem>
</ul>
</nav>
<div className="hidden md:block md:h-5 md:w-px md:bg-zinc-900/10 md:dark:bg-white/15" />
<div className="flex gap-4">
<div className="hidden md:block md:h-5 md:w-px md:bg-zinc-900/10 md:dark:bg-neutral-500/20" />
<div className="flex gap-2">
<MobileSearch />
<ModeToggle />
</div>

View File

@@ -3,30 +3,31 @@ import { GridPattern } from '@/components/GridPattern'
export function HeroPattern() {
return (
<div className="absolute inset-0 -z-10 mx-0 max-w-none overflow-hidden">
<div className="absolute left-1/2 top-0 ml-[-38rem] h-[25rem] w-[81.25rem] dark:[mask-image:linear-gradient(white,transparent)]">
<div className="absolute inset-0 bg-gradient-to-r from-[#FFAC1C] to-[#F28C28] opacity-40 [mask-image:radial-gradient(farthest-side_at_top,white,transparent)] dark:from-[#F28C28]/30 dark:to-[#FF7518]/30 dark:opacity-100">
<GridPattern
width={72}
height={56}
x="-12"
y="4"
squares={[
[4, 3],
[2, 1],
[7, 3],
[10, 6],
]}
className="absolute inset-x-0 inset-y-[-50%] h-[200%] w-full skew-y-[-18deg] fill-black/40 stroke-black/50 mix-blend-overlay dark:fill-white/2.5 dark:stroke-white/5"
/>
</div>
<div className="absolute inset-0 hidden dark:block z-0" style={{ backgroundColor: '#181A1D' }}>
<GridPattern
width={72}
height={56}
x="-12"
y="4"
squares={[
[4, 3],
[2, 1],
[7, 3],
[10, 6],
]}
className="absolute inset-x-0 inset-y-[-50%] h-[200%] w-full skew-y-[-18deg] fill-black/40 stroke-black/50 mix-blend-overlay dark:fill-white/2.5 dark:stroke-white/5"
/>
</div>
<div className="absolute inset-x-0 top-0 h-[25rem] dark:[mask-image:linear-gradient(white,transparent)] pointer-events-none z-10">
<svg
viewBox="0 0 1113 440"
preserveAspectRatio="xMidYMin slice"
aria-hidden="true"
className="absolute left-1/2 top-0 ml-[-19rem] w-[69.5625rem] fill-white blur-[26px] dark:hidden"
className="absolute left-1/2 top-0 -translate-x-1/2 w-full min-w-[69.5625rem] h-full fill-white blur-[26px] dark:hidden"
>
<path d="M.016 439.5s-9.5-300 434-300S882.516 20 882.516 20V0h230.004v439.5H.016Z" />
</svg>
</div>
</div>
)
}
}

View File

@@ -1,71 +0,0 @@
import { Button } from '@/components/Button'
import { Heading } from '@/components/Heading'
const howToGuides = [
{
href: '/get-started',
name: 'Quickstart guide',
description: 'Start using NetBird in under 5 minutes.',
},
{
href: '/manage/access-control/manage-network-access',
name: 'Manage network access',
description:
'Learn how to use access controls to manage access to your machines.',
},
{
href: '/manage/team/add-users-to-your-network',
name: 'Add users to your network',
description: 'Learn how to add team members to your NetBird network.',
},
{
href: '/manage/network-routes/routing-traffic-to-private-networks',
name: 'Route traffic to private networks',
description:
'Learn how to provide access to LANs, VPS, and corporate private networks.',
},
{
href: '/manage/network-routes/configuring-default-routes-for-internet-traffic',
name: 'Configure default routes and traffic for the Internet',
description: 'Understand how to set up your network for accessing the internet through default routes, also known as "exit nodes".',
},
{
href: '/manage/activity/traffic-events-logging',
name: 'Log and monitor network activity',
description:
'Learn how to keep track of system and network activities in your account.',
},
{
href: '/manage/dns',
name: 'Manage DNS in your network',
description:
'Learn how to configure name servers in your private network.',
},
]
export function HowToGuides() {
return (
<div className="my-16 xl:max-w-none">
<Heading level={2} id="guides">
How-To Guides
</Heading>
<div className="not-prose mt-4 grid grid-cols-1 gap-8 border-t border-zinc-900/5 pt-10 dark:border-white/5 sm:grid-cols-2 xl:grid-cols-4">
{howToGuides.map((guide) => (
<div key={guide.href}>
<h3 className="text-sm font-semibold text-zinc-900 dark:text-white">
{guide.name}
</h3>
<p className="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
{guide.description}
</p>
<p className="mt-4">
<Button href={guide.href} variant="text" arrow="right">
Read more
</Button>
</p>
</div>
))}
</div>
</div>
)
}

View File

@@ -183,15 +183,15 @@ export function Layout({ children, title, tableOfContents }) {
className="contents lg:pointer-events-none lg:fixed lg:inset-0 lg:z-40 lg:flex"
style={{ top: bannerHeight }}
>
<div className="contents lg:pointer-events-auto lg:block lg:w-72 lg:overflow-y-auto lg:border-r lg:border-zinc-900/10 lg:px-6 lg:pb-8 lg:pt-4 lg:dark:border-white/10 xl:w-80">
<div className="contents lg:pointer-events-auto lg:block lg:w-72 lg:overflow-y-auto lg:border-r lg:border-zinc-900/10 lg:dark:border-neutral-700/50 lg:px-6 lg:pb-8 lg:pt-4 lg:bg-white/70 lg:dark:bg-[#181A1D]/95 lg:backdrop-blur-lg xl:w-80 lg:overflow-x-visible">
<div className="hidden lg:flex">
<Link href="/" aria-label="Home">
<Logo className="h-6" />
</Link>
</div>
<Header />
{router.route.startsWith("/ipa") ? <NavigationAPI className="hidden lg:mt-10 lg:block" tableOfContents={tableOfContents} /> : <NavigationDocs className="hidden lg:mt-10 lg:block" />}
</div>
<Header />
</header>
<div className="min-w-0 max-w-2xl flex-auto px-4 py-16 lg:max-w-none lg:pl-8 lg:pr-0 xl:px-5">
<main className="py-16">

View File

@@ -78,7 +78,7 @@ export function MobileNavigation() {
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 top-14 bg-zinc-400/20 backdrop-blur-sm dark:bg-black/40" />
<div className="fixed inset-0 top-14 bg-zinc-400/20 backdrop-blur-sm dark:bg-[#181A1D]/40" />
</Transition.Child>
<Dialog.Panel>
@@ -105,7 +105,7 @@ export function MobileNavigation() {
>
<motion.div
layoutScroll
className="fixed bottom-0 left-0 top-14 w-full overflow-y-auto bg-white px-4 pb-4 pt-6 shadow-lg shadow-zinc-900/10 ring-1 ring-zinc-900/7.5 dark:bg-zinc-900 dark:ring-zinc-800 min-[416px]:max-w-sm sm:px-6 sm:pb-10"
className="fixed bottom-0 left-0 top-14 w-full overflow-y-auto bg-white/70 dark:bg-[#181A1D]/95 backdrop-blur-lg px-4 pb-4 pt-6 shadow-lg shadow-zinc-900/10 ring-1 ring-zinc-900/7.5 dark:ring-neutral-500/10 min-[416px]:max-w-sm sm:px-6 sm:pb-10"
>
{router.route.startsWith("/ipa") ? <NavigationAPI tableOfContents={[]} /> :
<NavigationDocs />}

90
src/components/Tiles.jsx Normal file
View File

@@ -0,0 +1,90 @@
import Link from 'next/link'
import { motion, useMotionTemplate, useMotionValue } from 'framer-motion'
import { GridPattern } from '@/components/GridPattern'
import { Heading } from '@/components/Heading'
function TilePattern({ mouseX, mouseY }) {
let maskImage = useMotionTemplate`radial-gradient(180px at ${mouseX}px ${mouseY}px, white, transparent)`
let style = { maskImage, WebkitMaskImage: maskImage }
return (
<div className="pointer-events-none">
<div className="absolute inset-0 rounded-2xl transition duration-300 [mask-image:linear-gradient(white,transparent)] group-hover:opacity-50">
<GridPattern
width={72}
height={56}
x="50%"
className="absolute inset-x-0 inset-y-[-30%] h-[160%] w-full skew-y-[-18deg] fill-black/[0.02] stroke-black/5 dark:fill-white/1 dark:stroke-white/2.5"
/>
</div>
<motion.div
className="absolute inset-0 rounded-2xl bg-gradient-to-r from-[#FFAC1C] to-[#F28C28] opacity-0 transition duration-300 group-hover:opacity-30 dark:group-hover:opacity-60 dark:from-[#F28C28]/30 dark:to-[#FF7518]/30"
style={style}
/>
<motion.div
className="absolute inset-0 rounded-2xl opacity-0 mix-blend-overlay transition duration-300 group-hover:opacity-100"
style={style}
>
<GridPattern
width={72}
height={56}
x="50%"
className="absolute inset-x-0 inset-y-[-30%] h-[160%] w-full skew-y-[-18deg] fill-black/50 stroke-black/70 dark:fill-white/2.5 dark:stroke-white/10"
/>
</motion.div>
</div>
)
}
/**
* Generic Tiles component for displaying a grid of tile items
*
* @param {string} title - The heading title for the tiles section
* @param {string} [id] - Optional id for the heading anchor
* @param {Array<{href: string, name: string, description: string}>} items - Array of tile items
* @param {string} [buttonText='Read more'] - Optional button text (defaults to "Read more")
*/
export function Tiles({ title, id, items, buttonText = 'Read more' }) {
return (
<div className="my-16 xl:max-w-none">
<Heading level={2} id={id} anchor={!!id}>
{title}
</Heading>
<div className="not-prose mt-4 grid grid-cols-1 gap-8 border-t border-zinc-900/5 pt-10 dark:border-white/5 sm:grid-cols-2">
{items.map((item) => {
let mouseX = useMotionValue(0)
let mouseY = useMotionValue(0)
function onMouseMove({ currentTarget, clientX, clientY }) {
let { left, top } = currentTarget.getBoundingClientRect()
mouseX.set(clientX - left)
mouseY.set(clientY - top)
}
return (
<div
key={item.href}
onMouseMove={onMouseMove}
className="group relative flex rounded-2xl bg-zinc-50 transition-shadow hover:shadow-md hover:shadow-zinc-900/5 dark:bg-white/2.5 dark:hover:shadow-black/5"
>
<TilePattern mouseX={mouseX} mouseY={mouseY} />
<div className="absolute inset-0 rounded-2xl ring-1 ring-inset ring-zinc-900/7.5 group-hover:ring-zinc-900/10 dark:ring-white/10 dark:group-hover:ring-white/20" />
<div className="relative rounded-2xl px-4 pb-4 pt-4">
<h3 className="text-sm font-semibold leading-7 text-zinc-900 dark:text-white">
<Link href={item.href}>
<span className="absolute inset-0 rounded-2xl" />
{item.name}
</Link>
</h3>
<p className="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
{item.description}
</p>
</div>
</div>
)
})}
</div>
</div>
)
}

123
src/components/YouTube.jsx Normal file
View File

@@ -0,0 +1,123 @@
import clsx from 'clsx'
/**
* Extracts YouTube video ID from various YouTube URL formats
*/
function extractYouTubeId(url) {
if (!url) return null
// If it's already just an ID, return it
if (!url.includes('youtube.com') && !url.includes('youtu.be') && !url.includes('/')) {
return url
}
// Handle youtu.be short links
if (url.includes('youtu.be/')) {
const match = url.match(/youtu\.be\/([a-zA-Z0-9_-]+)/)
return match ? match[1] : null
}
// Handle youtube.com/watch?v=...
if (url.includes('youtube.com/watch')) {
const match = url.match(/[?&]v=([a-zA-Z0-9_-]+)/)
return match ? match[1] : null
}
// Handle youtube.com/embed/...
if (url.includes('youtube.com/embed/')) {
const match = url.match(/embed\/([a-zA-Z0-9_-]+)/)
return match ? match[1] : null
}
return null
}
/**
* YouTube video embed component
*
* Usage:
* <YouTube videoId="CFa7SY4Up9k" />
* <YouTube url="https://www.youtube.com/watch?v=CFa7SY4Up9k" />
* <YouTube videoId="CFa7SY4Up9k" title="Video Title" />
* <YouTube videoId="CFa7SY4Up9k" start={175} />
* <YouTube videoId="CFa7SY4Up9k" color="white" modestbranding={1} />
*/
export function YouTube({
videoId,
url,
title,
start,
color = 'white', // 'red' | 'white' - controls progress bar color (default: white)
modestbranding = 1, // 0 | 1 - reduces YouTube branding (1 = minimal, default: 1 = minimal)
controls, // 0 | 1 | 2 - show/hide player controls (0 = hide, 1 = show, 2 = delayed)
rel = 1, // 0 | 1 - show related videos from same channel (0 = hide, 1 = show, default: 1 = show)
className,
...props
}) {
// Extract video ID from URL if provided
const id = videoId || extractYouTubeId(url)
if (!id) {
console.warn('YouTube component: No valid video ID or URL provided')
return null
}
// Extract start time from URL if present
let startTime = start
if (!startTime && url) {
const startMatch = url.match(/[?&]start=(\d+)/)
if (startMatch) {
startTime = parseInt(startMatch[1], 10)
}
}
// Build embed URL with parameters
let embedUrl = `https://www.youtube.com/embed/${id}`
const params = new URLSearchParams()
if (startTime) {
params.append('start', startTime.toString())
}
// Add color parameter - YouTube defaults to 'red', so we need to set 'white' explicitly
if (color === 'white') {
params.append('color', 'white')
}
if (modestbranding === 1) {
params.append('modestbranding', '1')
}
if (controls !== undefined && controls !== 1) {
params.append('controls', controls.toString())
}
// Only add rel parameter if explicitly set to 0 to hide related videos
// YouTube's default is 1 (show), so we don't need to add it when rel === 1
if (rel === 0) {
params.append('rel', '0')
}
const queryString = params.toString()
if (queryString) {
embedUrl += `?${queryString}`
}
return (
<div
className={clsx(
'relative w-full overflow-hidden my-6',
'ml-[10px] mr-auto',
'max-w-[40rem] lg:max-w-[50rem]',
className
)}
style={{ aspectRatio: '16 / 9' }}
{...props}
>
<iframe
src={embedUrl}
title={title || 'YouTube video player'}
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowFullScreen
className="absolute top-0 left-0 w-full h-full rounded-lg border-0"
/>
</div>
)
}

View File

@@ -2,11 +2,13 @@ import Link from 'next/link'
import clsx from 'clsx'
import { Heading } from '@/components/Heading'
import { YouTube } from '@/components/YouTube'
export const a = Link
export { Button } from '@/components/Button'
export { CodeGroup, Code as code, Pre as pre } from '@/components/Code'
export { Badge } from '@/components/Badge'
export { YouTube }
export const h2 = function H2(props) {
return <Heading level={2} {...props} />
@@ -40,6 +42,43 @@ function InfoIcon(props) {
)
}
function WarningIcon(props) {
return (
<svg viewBox="0 0 16 16" aria-hidden="true" {...props}>
<path
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
d="M8 3.5L3.5 12.5h9L8 3.5z"
/>
<circle cx="8" cy="9" r=".5" fill="none" />
<path
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
d="M8 6.5v1"
/>
</svg>
)
}
function SuccessIcon(props) {
return (
<svg viewBox="0 0 16 16" aria-hidden="true" {...props}>
<circle cx="8" cy="8" r="8" strokeWidth="0" />
<path
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
d="M5.5 8l2 2 3-3"
/>
</svg>
)
}
export function Note({ children }) {
return (
<div className="my-6 flex gap-2.5 rounded-l border border-orange-500/20 bg-orange-50/50 p-4 leading-6 text-orange-900 dark:border-orange-500/30 dark:bg-orange-500/5 dark:text-orange-200 dark:[--tw-prose-links-hover:theme(colors.orange.300)] dark:[--tw-prose-links:theme(colors.white)]">
@@ -51,6 +90,28 @@ export function Note({ children }) {
)
}
export function Warning({ children }) {
return (
<div className="my-6 flex gap-2.5 rounded-l border border-red-500/20 bg-red-50/50 p-4 leading-6 text-red-900 dark:border-red-500/30 dark:bg-red-500/5 dark:text-red-200 dark:[--tw-prose-links-hover:theme(colors.red.300)] dark:[--tw-prose-links:theme(colors.white)]">
<WarningIcon className="mt-1 h-4 w-4 flex-none fill-red-500 stroke-white dark:fill-red-200/20 dark:stroke-red-200" />
<div className="[&>:first-child]:mt-0 [&>:last-child]:mb-0">
{children}
</div>
</div>
)
}
export function Success({ children }) {
return (
<div className="my-6 flex gap-2.5 rounded-l border border-green-500/20 bg-green-50/50 p-4 leading-6 text-green-900 dark:border-green-500/30 dark:bg-green-500/5 dark:text-green-200 dark:[--tw-prose-links-hover:theme(colors.green.300)] dark:[--tw-prose-links:theme(colors.white)]">
<SuccessIcon className="mt-1 h-4 w-4 flex-none fill-green-500 stroke-white dark:fill-green-200/20 dark:stroke-green-200" />
<div className="[&>:first-child]:mt-0 [&>:last-child]:mb-0">
{children}
</div>
</div>
)
}
export function Row({ children }) {
return (
<div className="grid grid-cols-1 items-start gap-x-16 gap-y-10 xl:max-w-none xl:grid-cols-2">