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

211
README.md
View File

@@ -1,6 +1,6 @@
# The NetBird documentation # The NetBird documentation
This repository contains assets required to build the [documentation website for NetBird](https://netbird.io/docs/). It is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. This repository contains assets required to build the [documentation website for NetBird](https://netbird.io/docs/). It is built using [Next.js](https://nextjs.org/) with MDX support, a modern React framework for building static and dynamic websites.
We're glad that you want to contribute! We're glad that you want to contribute!
@@ -38,6 +38,215 @@ Furthermore, in some cases, one of your reviewers might ask for a technical revi
Participation in the NetBird community is governed by the [NetBirds' Code of Conduct](https://github.com/netbirdio/netbird/blob/main/CODE_OF_CONDUCT.md). Participation in the NetBird community is governed by the [NetBirds' Code of Conduct](https://github.com/netbirdio/netbird/blob/main/CODE_OF_CONDUCT.md).
## Components and Use
This documentation uses several custom MDX components. Here's a guide to the most commonly used components:
### Alert Components
Use these components to highlight important information:
#### Note
Displays informational content with an orange theme:
```mdx
import {Note} from "@/components/mdx"
<Note>
NetBird is an **[open-source](https://github.com/netbirdio/netbird)** project and can be self-hosted.
See a comparison between the self-hosted and cloud-hosted versions [here](/selfhosted/self-hosted-vs-cloud-netbird).
</Note>
```
#### Warning
Displays warning content with a red theme:
```mdx
import {Warning} from "@/components/mdx"
<Warning>
The API is still in Beta state so some errors might not be handled properly yet.
</Warning>
```
#### Success
Displays success messages with a green theme:
```mdx
import {Success} from "@/components/mdx"
<Success>
Your configuration has been successfully applied.
</Success>
```
### Tiles Component
Displays a grid of clickable cards with hover effects. Perfect for listing related resources or guides:
```mdx
import {Tiles} from "@/components/Tiles"
<Tiles
title="About NetBird"
id="about-netbird"
items={[
{
href: '/about-netbird/how-netbird-works',
name: 'How NetBird Works',
description: 'Learn about NetBird concepts, architecture, protocols, and how it creates secure networks.',
},
{
href: '/about-netbird/netbird-vs-traditional-vpn',
name: 'NetBird vs. Traditional VPN',
description: 'Discover how NetBird compares to traditional VPNs and understand the advantages of Zero Trust networking.',
},
]}
/>
```
**Props:**
- `title` (string, required): The heading title for the tiles section
- `id` (string, optional): Optional id for the heading anchor
- `items` (array, required): Array of objects with `href`, `name`, and `description`
- `buttonText` (string, optional): Button text (defaults to "Read more" - currently unused as cards are fully clickable)
### YouTube Component
Embeds YouTube videos with customizable parameters:
```mdx
import {YouTube} from "@/components/YouTube"
<YouTube videoId="CFa7SY4Up9k" />
// With custom parameters
<YouTube
videoId="CFa7SY4Up9k"
title="Video Title"
start={175}
color="white"
modestbranding={1}
rel={1}
/>
// Or use a URL instead of videoId
<YouTube url="https://www.youtube.com/watch?v=CFa7SY4Up9k" />
```
**Props:**
- `videoId` (string): YouTube video ID
- `url` (string): YouTube URL (alternative to videoId)
- `title` (string, optional): Video title
- `start` (number, optional): Start time in seconds
- `color` (string, optional): Progress bar color - `'white'` or `'red'` (default: `'white'`)
- `modestbranding` (number, optional): Reduces YouTube branding - `0` or `1` (default: `1`)
- `controls` (number, optional): Show/hide controls - `0`, `1`, or `2` (default: `1`)
- `rel` (number, optional): Show related videos - `0` or `1` (default: `1`)
### Button Component
Creates styled buttons with multiple variants:
```mdx
import {Button} from "@/components/Button"
// Primary button (default)
<Button href="https://app.netbird.io/install" arrow="right">
Get started
</Button>
// Secondary button
<Button href="/path" variant="secondary">
Learn more
</Button>
// Outline button
<Button href="/path" variant="outline">
Explore
</Button>
// Text button
<Button href="/path" variant="text" arrow="right">
Read more
</Button>
// With left arrow
<Button href="/path" arrow="left">
Back
</Button>
```
**Props:**
- `variant` (string, optional): Button style - `'primary'`, `'secondary'`, `'filled'`, `'outline'`, or `'text'` (default: `'primary'`)
- `href` (string, optional): Link URL (creates a link if provided, otherwise renders as button)
- `arrow` (string, optional): Arrow icon - `'left'` or `'right'`
- `children` (required): Button text content
### Other Common Components
#### Row and Col
Create two-column layouts:
```mdx
import {Row, Col} from "@/components/mdx"
<Row>
<Col>
Left column content
</Col>
<Col sticky>
Right column content (sticky on scroll)
</Col>
</Row>
```
#### Properties and Property
Define API properties or configuration options:
```mdx
import {Properties, Property} from "@/components/mdx"
<Properties>
<Property name="apiKey" type="string" required>
Your API key for authentication.
</Property>
<Property name="timeout" type="number" min={0} max={300}>
Request timeout in seconds (default: 30).
</Property>
</Properties>
```
#### Badge
Displays small status badges:
```mdx
import {Badge} from "@/components/mdx"
<Badge>New</Badge>
<Badge variant="secondary">Beta</Badge>
```
#### Code Blocks
Code syntax highlighting (automatically available):
```mdx
\`\`\`bash
npm install
npm run dev
\`\`\`
// Or use code groups for multiple languages
<CodeGroup>
```bash title="Installation"
npm install
```
```yarn title="Installation"
yarn install
```
</CodeGroup>
```
## Thank you ## Thank you
NetBird thrives on community participation, and we appreciate your contributions to our website and our documentation! NetBird thrives on community participation, and we appreciate your contributions to our website and our documentation!

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 = { const variantStyles = {
primary: 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: 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', '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: 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: 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', '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({ export function Button({
@@ -36,7 +36,7 @@ export function Button({
let Component = props.href ? Link : 'button' let Component = props.href ? Link : 'button'
className = clsx( 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], variantStyles[variant],
className 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 ( return (
<Component className={className} {...props}> <Component className={className} {...props}>
{arrow === 'left' && arrowIcon} {arrow === 'left' && arrowIcon}

View File

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

View File

@@ -3,30 +3,31 @@ import { GridPattern } from '@/components/GridPattern'
export function HeroPattern() { export function HeroPattern() {
return ( return (
<div className="absolute inset-0 -z-10 mx-0 max-w-none overflow-hidden"> <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 hidden dark:block z-0" style={{ backgroundColor: '#181A1D' }}>
<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
<GridPattern width={72}
width={72} height={56}
height={56} x="-12"
x="-12" y="4"
y="4" squares={[
squares={[ [4, 3],
[4, 3], [2, 1],
[2, 1], [7, 3],
[7, 3], [10, 6],
[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"
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> <div className="absolute inset-x-0 top-0 h-[25rem] dark:[mask-image:linear-gradient(white,transparent)] pointer-events-none z-10">
<svg <svg
viewBox="0 0 1113 440" viewBox="0 0 1113 440"
preserveAspectRatio="xMidYMin slice"
aria-hidden="true" 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" /> <path d="M.016 439.5s-9.5-300 434-300S882.516 20 882.516 20V0h230.004v439.5H.016Z" />
</svg> </svg>
</div> </div>
</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" className="contents lg:pointer-events-none lg:fixed lg:inset-0 lg:z-40 lg:flex"
style={{ top: bannerHeight }} 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"> <div className="hidden lg:flex">
<Link href="/" aria-label="Home"> <Link href="/" aria-label="Home">
<Logo className="h-6" /> <Logo className="h-6" />
</Link> </Link>
</div> </div>
<Header />
{router.route.startsWith("/ipa") ? <NavigationAPI className="hidden lg:mt-10 lg:block" tableOfContents={tableOfContents} /> : <NavigationDocs className="hidden lg:mt-10 lg:block" />} {router.route.startsWith("/ipa") ? <NavigationAPI className="hidden lg:mt-10 lg:block" tableOfContents={tableOfContents} /> : <NavigationDocs className="hidden lg:mt-10 lg:block" />}
</div> </div>
<Header />
</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"> <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"> <main className="py-16">

View File

@@ -78,7 +78,7 @@ export function MobileNavigation() {
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" 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> </Transition.Child>
<Dialog.Panel> <Dialog.Panel>
@@ -105,7 +105,7 @@ export function MobileNavigation() {
> >
<motion.div <motion.div
layoutScroll 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={[]} /> : {router.route.startsWith("/ipa") ? <NavigationAPI tableOfContents={[]} /> :
<NavigationDocs />} <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 clsx from 'clsx'
import { Heading } from '@/components/Heading' import { Heading } from '@/components/Heading'
import { YouTube } from '@/components/YouTube'
export const a = Link export const a = Link
export { Button } from '@/components/Button' export { Button } from '@/components/Button'
export { CodeGroup, Code as code, Pre as pre } from '@/components/Code' export { CodeGroup, Code as code, Pre as pre } from '@/components/Code'
export { Badge } from '@/components/Badge' export { Badge } from '@/components/Badge'
export { YouTube }
export const h2 = function H2(props) { export const h2 = function H2(props) {
return <Heading level={2} {...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 }) { export function Note({ children }) {
return ( 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)]"> <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 }) { export function Row({ children }) {
return ( return (
<div className="grid grid-cols-1 items-start gap-x-16 gap-y-10 xl:max-w-none xl:grid-cols-2"> <div className="grid grid-cols-1 items-start gap-x-16 gap-y-10 xl:max-w-none xl:grid-cols-2">

View File

@@ -44,7 +44,7 @@ export default function Document() {
<script dangerouslySetInnerHTML={{ __html: modeScript }} /> <script dangerouslySetInnerHTML={{ __html: modeScript }} />
<link rel="shortcut icon" href="/docs-static/img/favicon.ico" /> <link rel="shortcut icon" href="/docs-static/img/favicon.ico" />
</Head> </Head>
<body className="bg-white antialiased dark:bg-zinc-900"> <body className="bg-white antialiased dark:bg-[#181A1D]">
<GoogleTageManagerBodyScript /> <GoogleTageManagerBodyScript />
<Main /> <Main />
<NextScript /> <NextScript />

View File

@@ -7,9 +7,7 @@ export const title = 'Getting Started'
Welcome to NetBird! This guide will walk you through our new onboarding process to create your account, connect your first devices, Welcome to NetBird! This guide will walk you through our new onboarding process to create your account, connect your first devices,
and build a secure peer-to-peer overlay network in less than ten minutes. and build a secure peer-to-peer overlay network in less than ten minutes.
<div className="videowrapper"> <YouTube videoId="dr0u-u9uD84" />
<iframe src="https://www.youtube.com/embed/dr0u-u9uD84" allow="fullscreen;"></iframe>
</div>
## Create Your Account ## Create Your Account

View File

@@ -4,9 +4,7 @@ The NetBird client (agent) allows a peer to join a pre-existing NetBird deployme
there are both managed and [self-hosted](https://docs.netbird.io/selfhosted/selfhosted-quickstart) options available. there are both managed and [self-hosted](https://docs.netbird.io/selfhosted/selfhosted-quickstart) options available.
<div className="videowrapper"> <YouTube videoId="AK0Ct-ULFKg" start={669} />
<iframe src="https://www.youtube.com/embed/AK0Ct-ULFKg?start=669" allow="fullscreen;"></iframe>
</div>
<Note> <Note>
The NetBird package is officially included starting from OPNsense `25.7.3`. The NetBird package is officially included starting from OPNsense `25.7.3`.

View File

@@ -7,9 +7,7 @@ there are both managed and [self-hosted](https://docs.netbird.io/selfhosted/self
This installation is intended for early adopters while the pfSense package is under review and not yet available in the pfSense package manager. This installation is intended for early adopters while the pfSense package is under review and not yet available in the pfSense package manager.
</Note> </Note>
<div className="videowrapper"> <YouTube videoId="Kgrcquyeohc" />
<iframe src="https://www.youtube.com/embed/Kgrcquyeohc" allow="fullscreen;"></iframe>
</div>
## Prerequisites ## Prerequisites
- Shell/SSH access to pfSense (via Web UI shell or remote SSH) - Shell/SSH access to pfSense (via Web UI shell or remote SSH)

View File

@@ -110,9 +110,7 @@ Now you should see a successful connection message and you're good to go! Now if
## Proxmox Setup Tutorial ## Proxmox Setup Tutorial
<div className="videowrapper"> <YouTube videoId="KMNS_JoHFhg" />
<iframe src="https://www.youtube.com/embed/KMNS_JoHFhg" allow="fullscreen;"></iframe>
</div>
## Additional Resources ## Additional Resources

View File

@@ -2,9 +2,7 @@ import {Note} from "@/components/mdx";
# Install NetBird on the Raspberry Pi # Install NetBird on the Raspberry Pi
<div className="videowrapper"> <YouTube videoId="P0aAdYnex80" />
<iframe src="https://www.youtube.com/embed/P0aAdYnex80" allow="fullscreen;"></iframe>
</div>
Start by downloading [Raspberry Pi Imager](https://www.raspberrypi.com/software/) for your operating system and inserting a microSD card with at least 8GB of capacity, though 32GB is recommended for breathing room. Start by downloading [Raspberry Pi Imager](https://www.raspberrypi.com/software/) for your operating system and inserting a microSD card with at least 8GB of capacity, though 32GB is recommended for breathing room.

View File

@@ -120,9 +120,7 @@ For a complete cleanup, you should also remove the Synology NAS as a peer from y
## Video Walkthrough ## Video Walkthrough
<div className="videowrapper"> <YouTube videoId="9VKOAe_T038" />
<iframe src="https://www.youtube.com/embed/9VKOAe_T038" allow="fullscreen;"></iframe>
</div>
## Support Us ## Support Us

View File

@@ -1,15 +1,14 @@
import {Note} from "@/components/mdx" import {Note} from "@/components/mdx"
import {HowToGuides} from "@/components/How-To-Guides" import {Tiles} from "@/components/Tiles"
import {AboutNetbird} from "@/components/AboutNetbird" import {YouTube} from "@/components/YouTube"
import {Button} from "@/components/Button"
export const description = export const description =
'Learn everything there is to know about NetBird.' 'Learn everything there is to know about NetBird.'
# Introduction to NetBird # Introduction to NetBird
<div className="videowrapper"> <YouTube videoId="CFa7SY4Up9k" />
<iframe src="https://www.youtube.com/embed/CFa7SY4Up9k?si=FVdoVW0ClxsJgd4t" allow="fullscreen;"></iframe>
</div>
NetBird is an Open Source Zero Trust Networking platform that allows you to create secure private networks for your NetBird is an Open Source Zero Trust Networking platform that allows you to create secure private networks for your
organization or home. We designed NetBird to be simple and fast, requiring near-zero configuration effort and leaving organization or home. We designed NetBird to be simple and fast, requiring near-zero configuration effort and leaving
@@ -30,8 +29,85 @@ It literally takes less than 5 minutes to deploy a secure point-to-point VPN wit
<Button href="https://github.com/netbirdio/netbird" variant="outline" children="Explore Github" /> <Button href="https://github.com/netbirdio/netbird" variant="outline" children="Explore Github" />
</div> </div>
<AboutNetbird /> <Tiles
title="About NetBird"
id="about-netbird"
items={[
{
href: '/about-netbird/how-netbird-works',
name: 'How NetBird Works',
description: 'Learn about NetBird concepts, architecture, protocols, and how it creates secure networks.',
},
{
href: '/about-netbird/netbird-vs-traditional-vpn',
name: 'NetBird vs. Traditional VPN',
description:
'Discover how NetBird compares to traditional VPNs and understand the advantages of Zero Trust networking.',
},
{
href: '/about-netbird/why-wireguard-with-netbird',
name: 'Why WireGuard with NetBird',
description:
'Explore why NetBird uses WireGuard and how it provides fast, secure, and modern networking.',
},
{
href: '/about-netbird/browser-client-architecture',
name: 'Browser Client Architecture',
description:
'Understand how the Browser Client enables secure remote access directly from web browsers using WebAssembly.',
},
]}
/>
<HowToGuides /> <Tiles
title="Guides"
id="guides"
items={[
{
href: '/get-started',
name: 'Quickstart Guide',
description: 'Get started with NetBird in under 5 minutes. Learn the basics of installation and setup.',
},
{
href: '/selfhosted/selfhosted-quickstart',
name: 'Self-Hosted Quickstart',
description: 'Get started with self-hosted NetBird in 5 minutes. Learn how to deploy and configure your own NetBird instance.',
},
{
href: '/manage/access-control/manage-network-access',
name: 'Manage Network Access',
description:
'Learn how to use access control policies to manage and secure access to your machines and resources.',
},
{
href: '/manage/team/add-users-to-your-network',
name: 'Add Users to Your Network',
description: 'Discover how to add team members to your NetBird network and manage user access.',
},
{
href: '/manage/network-routes/routing-traffic-to-private-networks',
name: 'Route Traffic to Private Networks',
description:
'Learn how to provide secure access to LANs, VPS instances, and corporate private networks.',
},
{
href: '/manage/network-routes/configuring-default-routes-for-internet-traffic',
name: 'Configure Default Routes',
description: 'Set up default routes for internet traffic and configure exit nodes for your network.',
},
{
href: '/manage/activity/traffic-events-logging',
name: 'Log and Monitor Network Activity',
description:
'Learn how to track and monitor system and network activities in your NetBird account.',
},
{
href: '/manage/dns',
name: 'Manage DNS in Your Network',
description:
'Configure custom name servers and DNS settings for your private network.',
},
]}
/>

View File

@@ -1,8 +1,6 @@
# Allow Only Intune-Managed Devices to Access Your Network # Allow Only Intune-Managed Devices to Access Your Network
<div className="videowrapper"> <YouTube videoId="W4DaE4Dj04o" />
<iframe src="https://www.youtube.com/embed/W4DaE4Dj04o" allow="fullscreen;"></iframe>
</div>
<Note> <Note>
TLDR: Devices marked as "Non-compliant" in Intune will automatically lose access, ensuring strict adherence to your security policies. TLDR: Devices marked as "Non-compliant" in Intune will automatically lose access, ensuring strict adherence to your security policies.

View File

@@ -10,9 +10,7 @@ The integration of NetBird with SentinelOne provides organizations with robust s
only IT-managed devices running SentinelOne to access the network. Additionally, the integration uses SentinelOne's threat only IT-managed devices running SentinelOne to access the network. Additionally, the integration uses SentinelOne's threat
detection capabilities, enabling administrators to further limit network access based on the security posture of each device. detection capabilities, enabling administrators to further limit network access based on the security posture of each device.
<div className="videowrapper"> <YouTube videoId="QVs0RhprVYM" />
<iframe src="https://www.youtube.com/embed/QVs0RhprVYM" allow="fullscreen;"></iframe>
</div>
SentinelOne's endpoint protection provides real-time threat detection and automated response capabilities. By integrating with SentinelOne's endpoint protection provides real-time threat detection and automated response capabilities. By integrating with
SentinelOne Singularity, NetBird can ensure that only devices with active security monitoring and protection can access the network. SentinelOne Singularity, NetBird can ensure that only devices with active security monitoring and protection can access the network.

View File

@@ -8,9 +8,7 @@ your environment.
Watch our Access Control video on YouTube: Watch our Access Control video on YouTube:
<div className="videowrapper"> <YouTube videoId="WtZD_q-g_Jc" />
<iframe src="https://www.youtube.com/embed/WtZD_q-g_Jc" allow="fullscreen;"></iframe>
</div>
<Note> <Note>
For a visual overview of your access policies and network topology, check out the [Control Center](/manage/control-center), which provides an interactive graph view of peers, groups, and their access relationships. For a visual overview of your access policies and network topology, check out the [Control Center](/manage/control-center), which provides an interactive graph view of peers, groups, and their access relationships.

View File

@@ -10,9 +10,7 @@ By using these diverse checking capabilities, NetBird empowers you to create a r
## Setting Up Posture Checks ## Setting Up Posture Checks
Setting up posture checks in NetBird is straightforward, you can follow the example in the video below: Setting up posture checks in NetBird is straightforward, you can follow the example in the video below:
<div className="videowrapperadjusted" > <YouTube videoId="-KlJUBuZrpo" />
<iframe src="https://www.youtube.com/embed/-KlJUBuZrpo" allow="fullscreen;"></iframe>
</div>
Or follow the guide with other examples below: Or follow the guide with other examples below:

View File

@@ -8,7 +8,7 @@ and many other key network events.
To get started with event logging in NetBird, watch this introductory video: To get started with event logging in NetBird, watch this introductory video:
<iframe width="560" height="315" src="https://www.youtube.com/embed/UlnMo1KYXPU?si=JdzEr9v2EZHlP7lc" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> <YouTube videoId="UlnMo1KYXPU" />
## Access the Audit Events Logging View ## Access the Audit Events Logging View

View File

@@ -1,9 +1,7 @@
# Routing Traffic to Private Networks # Routing Traffic to Private Networks
<div className="videowrapper"> <YouTube videoId="VQuPuBOAknQ" />
<iframe src="https://www.youtube.com/embed/VQuPuBOAknQ" allow="fullscreen;"></iframe>
</div>
<br/><br/> <br/><br/>

View File

@@ -10,7 +10,7 @@ an Android or iOS device, a personal laptop, a single-board computer like Raspbe
## Related Video Content ## Related Video Content
For details on adding machines to your network, part of our "Getting started with NetBird" video covers this topic: For details on adding machines to your network, part of our "Getting started with NetBird" video covers this topic:
<iframe width="560" height="315" src="https://www.youtube.com/embed/JRCZy4rLi-c?start=34" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> <YouTube videoId="JRCZy4rLi-c" start={34} />
## Use NetBird web UI to add new peers ## Use NetBird web UI to add new peers

View File

@@ -14,7 +14,7 @@ Administrators then can assess whether the peer is eligible to join the network.
For details on the peer approval feature, part of our "Getting started with NetBird" video covers this topic: For details on the peer approval feature, part of our "Getting started with NetBird" video covers this topic:
<iframe width="560" height="315" src="https://www.youtube.com/embed/JRCZy4rLi-c?start=335" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> <YouTube videoId="JRCZy4rLi-c" start={335} />
## Enable peer approval ## Enable peer approval
To enable peer approval, navigate to [Settings &raquo; Authentication](https://app.netbird.io/settings) and enable 'Peer approval'. To enable peer approval, navigate to [Settings &raquo; Authentication](https://app.netbird.io/settings) and enable 'Peer approval'.

View File

@@ -6,7 +6,7 @@ It simply associates a machine with an account on a first run.
## Related Video Content ## Related Video Content
For a comprehensive guide, part of our "Getting started with NetBird" video specifically covers setup keys: For a comprehensive guide, part of our "Getting started with NetBird" video specifically covers setup keys:
<iframe width="560" height="315" src="https://www.youtube.com/embed/JRCZy4rLi-c?start=175" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> <YouTube videoId="JRCZy4rLi-c" start={175} />
The setup key can be provided as a parameter to the ```netbird up``` command. The setup key can be provided as a parameter to the ```netbird up``` command.
This makes it possible to run automated deployments with infrastructure-as-code software like Ansible, Cloudformation or Terraform. This makes it possible to run automated deployments with infrastructure-as-code software like Ansible, Cloudformation or Terraform.

View File

@@ -26,9 +26,7 @@ eliminating the need for manual grouping.
This video guide walks you through an example integration with Microsoft Entra ID, covering both user onboarding and This video guide walks you through an example integration with Microsoft Entra ID, covering both user onboarding and
offboarding scenarios: offboarding scenarios:
<div className="videowrapper"> <YouTube videoId="RxYWTpf7cgY" />
<iframe src="https://www.youtube.com/embed/RxYWTpf7cgY" allow="fullscreen;"></iframe>
</div>
## Supported Identity Providers ## Supported Identity Providers

View File

@@ -59,68 +59,6 @@
overflow:hidden; overflow:hidden;
} }
.videowrapper {
position: relative;
width: 100%;
aspect-ratio: 16 / 9;
overflow: hidden;
margin-left: 10px;
margin-right: auto;
}
@media (max-width: 1024px) {
.videowrapper {
max-width: 40rem; /* matches prose max-width-2xl */
}
}
@media (min-width: 1024px) {
.videowrapper {
max-width: 50rem; /* matches prose max-width-3xl */
}
}
.videowrapper iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 4px;
border: 0;
}
.videowrapperadjusted {
position: relative;
width: 100%;
aspect-ratio: 16 / 9;
overflow: hidden;
margin-left: 10px;
margin-right: auto;
}
@media (max-width: 1024px) {
.videowrapperadjusted {
max-width: 40rem; /* matches prose max-width-2xl */
}
}
@media (min-width: 1024px) {
.videowrapperadjusted {
max-width: 50rem; /* matches prose max-width-3xl */
}
}
.videowrapperadjusted iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 4px;
border: 0;
}
.spacer-sm { .spacer-sm {
height: 5px; height: 5px;
} }