1import {2 Sections,3 SectionsItem,4 SectionsListItem,5} from "@/components/features/pxl/newspaper/sections";6import type { Opml } from "@/lib/schemas/pxl/opml";7
8const sources: Opml.Outline[] = [9 {10 text: "Local",11 outlines: [12 {13 text: "EL PAÍS",14 description:15 "Noticias de última hora sobre la actualidad en España y el mundo: política, economía, deportes, cultura, sociedad, tecnología, gente, opinión, viajes, moda, televisión, los blogs y las firmas de EL PAÍS. Además especiales, vídeos, fotos, audios, gráficos, entrevistas, promociones y todos los servicios de EL PAÍS.",16 },17 ],18 },19 {20 text: "World",21 outlines: [22 {23 text: "CNBC",24 description:25 "CNBC International is the world leader for news on business, technology, China, trade, oil prices, the Middle East and markets.",26 },27
28 {29 text: "NDTV",30 description:31 "NDTV.com provides the latest information from and in-depth coverage of India and the world. Find breaking news, India news, top stories, elections, politics, business, cricket, movies, lifestyle, health, live TV, videos, photos and more.",32 },33 {34 text: "New York Times",35 },36 ],37 },38 {39 text: "Culture",40 outlines: [41 {42 text: "Mythology",43 outlines: [44 {45 text: "Devdutt Pattanaik Myths",46 description:47 "Devdutt Pattanaik writes on relevance of mythology in modern times, especially in areas of management, governance and leadership. He defines mythology as cultural truths revealed through stories, symbols and rituals.",48 },49 ],50 },51 {52 text: "History",53 outlines: [54 {55 text: "JSTOR Daily",56 },57 ],58 },59 {60 text: "Science",61 outlines: [62 {63 text: "Nature",64 description:65 "Nature is the foremost international weekly scientific journal in the world and is the flagship journal for Nature Portfolio. It publishes the finest peer-reviewed research in all fields of science and technology on the basis of its originality, importance, interdisciplinary interest, timeliness, accessibility, elegance and surprising conclusions. Nature publishes landmark papers, award winning news, leading comment and expert opinion on important, topical scientific news and events that enable readers to share the latest discoveries in science and evolve the discussion amongst the global scientific community.",66 },67 {68 text: "Phys.org",69 description:70 "Phys.org internet news portal provides the latest news on science including: Physics, Nanotechnology, Life Sciences, Space Science, Earth Science, Environment, Health and Medicine.",71 },72 ],73 },74 ],75 },76 {77 text: "Tech",78 outlines: [79 {80 text: "Technology",81 outlines: [82 {83 text: "MIT News",84 },85 {86 text: "CNET News",87 description:88 "CNET news editors and reporters provide top technology news, with investigative reporting and in-depth coverage of tech issues and events.",89 },90 ],91 },92 {93 text: "DIY",94 outlines: [95 {96 text: "Doityourself.com",97 description:98 "Do it yourself home improvement and diy repair at Doityourself.com. Includes home improvement projects, home repair, kitchen remodeling, plumbing, electrical, painting, real estate, and decorating",99 },100 ],101 },102 ],103 },104 {105 text: "Lifestyle",106 },107 {108 text: "Entertainment",109 },110];111
112export default function SectionsDemo() {113 return (114 <Sections>115 {sources.map((s) => (116 <SectionsItem.Outline key={s.text} outline={s}>117 {s.outlines?.map((outline) => (118 <SectionsListItem.Outline key={outline.text} outline={outline} />119 ))}120 </SectionsItem.Outline>121 ))}122 </Sections>123 );124}Installation
Section titled “Installation”pnpm dlx shadcn@latest add pxl-ui/registry/newspaper/sections
Copy and paste the following code into your project.
1import { NavigationMenu as NavigationMenuPrimitive } from "@base-ui/react/navigation-menu";2import type {3 ComponentPropsWithoutRef,4 ComponentPropsWithRef,5} from "react";6
7import type { Outline } from "@/lib/schemas/pxl/opml";8import { cn } from "@/lib/utils";9
10function SectionsPositioner({11 className,12 side = "bottom",13 sideOffset = 8,14 align = "start",15 alignOffset = 0,16 ...props17}: NavigationMenuPrimitive.Positioner.Props) {18 return (19 <NavigationMenuPrimitive.Portal>20 <NavigationMenuPrimitive.Positioner21 side={side}22 sideOffset={sideOffset}23 align={align}24 alignOffset={alignOffset}25 className={cn(26 "isolate z-50 h-(--positioner-height) w-(--positioner-width) max-w-(--available-width) transition-[top,left,right,bottom] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] data-instant:transition-none data-[side=bottom]:before:-top-2.5 data-[side=bottom]:before:right-0 data-[side=bottom]:before:left-0",27 className,28 )}29 {...props}30 >31 <NavigationMenuPrimitive.Popup className="data-[ending-style]:easing-[ease] xs:w-(--popup-width) relative h-(--popup-height) w-(--popup-width) origin-(--transform-origin) pixel-border pixel-size-lg bg-popover text-popover-foreground shadow ring-1 ring-foreground/10 transition-[opacity,transform,width,height,scale,translate] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] outline-none data-ending-style:scale-90 data-ending-style:opacity-0 data-ending-style:duration-150 data-starting-style:scale-90 data-starting-style:opacity-0">32 <NavigationMenuPrimitive.Viewport className="relative size-full overflow-hidden" />33 </NavigationMenuPrimitive.Popup>34 </NavigationMenuPrimitive.Positioner>35 </NavigationMenuPrimitive.Portal>36 );37}38
39
40function SectionsList({41 className,42 ...props43}: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.List>) {44 return (45 <NavigationMenuPrimitive.List46 data-slot="navigation-menu-list"47 className={cn(48 "group flex flex-1 list-none items-center justify-center gap-0",49 className,50 )}51 {...props}52 />53 );54}55
56function Sections({57 align = "start",58 className,59 children,60 ...props61}: NavigationMenuPrimitive.Root.Props &62 Pick<NavigationMenuPrimitive.Positioner.Props, "align">) {63 return (64 <NavigationMenuPrimitive.Root65 data-slot="navigation-menu"66 className={cn(67 "group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",68 className,69 )}70 {...props}71 >72 <SectionsList>73 {children}74 </SectionsList>75 <SectionsPositioner align={align} />76 </NavigationMenuPrimitive.Root>77 );78}79
80function SectionsLink({81 className,82 ...props83}: NavigationMenuPrimitive.Link.Props) {84 return (85 <NavigationMenuPrimitive.Link86 data-slot="navigation-menu-link"87 className={cn(88 "font-serif flex items-center gap-2 pixel-rounded pixel-size-md p-2 text-sm transition-all outline-none hover:bg-muted focus:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 in-data-[slot=navigation-menu-content]:rounded-md data-active:bg-muted/50 data-active:hover:bg-muted data-active:focus:bg-muted [&_svg:not([class*='size-'])]:size-4",89 className,90 )}91 {...props}92 />93 );94}95
96function SectionsTrigger({97 className,98 children,99 ...props100}: NavigationMenuPrimitive.Trigger.Props) {101 return (102 <NavigationMenuPrimitive.Trigger103 data-slot="navigation-menu-trigger"104 className={cn(105 "group/navigation-menu-trigger inline-flex font-serif h-9 w-max items-center justify-center pixel-rounded pixel-rounded-md px-2.5 py-1.5 text-sm font-medium transition-all outline-none hover:bg-muted focus:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-popup-open:bg-muted/50 data-popup-open:hover:bg-muted data-open:bg-muted/50 data-open:hover:bg-muted data-open:focus:bg-muted",106 "group",107 className,108 )}109 {...props}110 >111 {children}{" "}112 <svg113 xmlns="http://www.w3.org/2000/svg"114 fill="currentColor"115 viewBox="0 0 24 24"116 className="relative top-px ml-1 size-3 transition duration-300 group-data-popup-open/navigation-menu-trigger:rotate-180 group-data-open/navigation-menu-trigger:rotate-180"117 aria-hidden="true"118 >119 <path d="M13 16h-2v-2h2v2Zm-2-2H9v-2h2v2Zm4 0h-2v-2h2v2Zm-6-2H7v-2h2v2Zm8 0h-2v-2h2v2ZM7 10H5V8h2v2Zm12 0h-2V8h2v2Z" />120 </svg>121 </NavigationMenuPrimitive.Trigger>122 );123}124
125function SectionsContent({126 className,127 ...props128}: NavigationMenuPrimitive.Content.Props) {129 return (130 <NavigationMenuPrimitive.Content131 data-slot="navigation-menu-content"132 className={cn(133 "data-ending-style:data-activation-direction=left:translate-x-[50%] data-ending-style:data-activation-direction=right:translate-x-[-50%] data-starting-style:data-activation-direction=left:translate-x-[-50%] data-starting-style:data-activation-direction=right:translate-x-[50%] h-full w-auto p-[calc(var(--pixel-size)+(--spacing(1)))] transition-[opacity,transform,translate] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] group-data-[viewport=false]/navigation-menu:pixel-rounded group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:ring-1 group-data-[viewport=false]/navigation-menu:ring-foreground/10 group-data-[viewport=false]/navigation-menu:duration-300 data-ending-style:opacity-0 data-starting-style:opacity-0 data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 data-[motion^=from-]:animate-in data-[motion^=from-]:fade-in data-[motion^=to-]:animate-out data-[motion^=to-]:fade-out **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none group-data-[viewport=false]/navigation-menu:data-open:animate-in group-data-[viewport=false]/navigation-menu:data-open:fade-in-0 group-data-[viewport=false]/navigation-menu:data-open:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-closed:animate-out group-data-[viewport=false]/navigation-menu:data-closed:fade-out-0 group-data-[viewport=false]/navigation-menu:data-closed:zoom-out-95",134 className,135 )}136 {...props}137 />138 );139}140
141function SectionsItem({142 children,143 title,144 href,145 className,146 ...props147}: ComponentPropsWithRef<typeof NavigationMenuPrimitive.Item> & {148 href: string;149}) {150 return (151 <NavigationMenuPrimitive.Item152 data-slot="navigation-menu-item"153 className={cn("relative", className)}154 {...props}155 >156 {children ? (157 <NavigationMenuPrimitive.Link158 href={href}159 render={160 <a href={href}>161 <SectionsTrigger>{title}</SectionsTrigger>162 <SectionsContent>163 <ul className="w-96">164 {children}165 </ul>166 </SectionsContent>167 </a>168 }169 />170 ) : (171 <SectionsLink href={href}>{title}</SectionsLink>172 )}173 </NavigationMenuPrimitive.Item>174 );175}176
177function SectionsListItem({178 title,179 children,180 href,181 className,182 ...props183}: ComponentPropsWithoutRef<"li"> & { href: string }) {184 return (185 <li {...props}>186 <NavigationMenuPrimitive.Link187 data-slot="navigation-menu-link"188 className={cn(189 "flex items-center gap-2 pixel-rounded pixel-size-md p-2 text-sm transition-all outline-none hover:bg-muted focus:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 in-data-[slot=navigation-menu-content]:rounded-md data-active:bg-muted/50 data-active:hover:bg-muted data-active:focus:bg-muted [&_svg:not([class*='size-'])]:size-4",190 className,191 )}192 render={193 <a href={href}>194 <div className="font-serif flex flex-col gap-1 text-sm">195 <div className="leading-none font-medium">{title}</div>196 <div className="line-clamp-2 text-muted-foreground">197 {children}198 </div>199 </div>200 </a>201 }202 />203 </li>204 );205}206
207function OutlineSectionsItem({208 children,209 outline,210}: ComponentPropsWithRef<typeof NavigationMenuPrimitive.Item> & {211 outline: Outline;212}) {213 return (214 <SectionsItem215 href={outline.url ?? "#"}216 title={outline.title ?? outline.text}217 >218 {children}219 </SectionsItem>220 );221}222SectionsItem.Outline = OutlineSectionsItem;223
224function OutlineSectionsListItem({225 outline,226}: ComponentPropsWithoutRef<"li"> & { outline: Outline }) {227 return (228 <SectionsListItem229 href={outline.url ?? "#"}230 title={outline.title ?? outline.text}231 >232 {outline.description}233 {outline.outlines?.map((o) => o.text).join(", ")}234 </SectionsListItem>235 );236}237SectionsListItem.Outline = OutlineSectionsListItem;238
239export {240 Sections,241 SectionsItem,242 SectionsListItem243};