mirror of
https://github.com/netbirdio/docs.git
synced 2026-04-15 23:16:36 +00:00
Style Consistency Changes and Element Improvements (#541)
Co-authored-by: braginini <bangvalo@gmail.com>
This commit is contained in:
211
README.md
211
README.md
@@ -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!
|
||||||
@@ -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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -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}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ 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}
|
||||||
@@ -19,10 +18,12 @@ export function HeroPattern() {
|
|||||||
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>
|
||||||
|
|||||||
@@ -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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -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">
|
||||||
|
|||||||
@@ -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
90
src/components/Tiles.jsx
Normal 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
123
src/components/YouTube.jsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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">
|
||||||
|
|||||||
@@ -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 />
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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`.
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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.',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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/>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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 » Authentication](https://app.netbird.io/settings) and enable 'Peer approval'.
|
To enable peer approval, navigate to [Settings » Authentication](https://app.netbird.io/settings) and enable 'Peer approval'.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user