Shared UI Components
Relevant source files
The following files were used as context for generating this wiki page:
- packages/shared/components/sheet.tsx
- packages/shared/global.css
- package.json
- packages/web/src/components/page-card.tsx
- packages/plugin/popup/components/HistoryTaskList.tsx
- packages/web/src/pages/(layout)/page.[slug].tsx
- packages/web/src/components/readability-page-content.tsx
- packages/web/src/pages/showcase/page.[slug].tsx
The web-archive project utilizes a shared UI component library located in packages/shared to ensure a consistent look and feel across its different applications, primarily the web interface and the plugin's popup UI. This library is built with a modern frontend stack, prioritizing accessibility, customizability, and developer experience.
The core philosophy is to build upon unstyled, accessible primitives, which are then styled using a utility-first CSS framework. This approach allows for rapid development of new UI features while maintaining a cohesive design system. The components are composed to create complex UI elements like page cards and settings panels, which are used throughout the project.
Core Technologies
The shared component library is built upon a foundation of several key technologies that work together to provide a robust and flexible system.
- React: The library is built as a set of React components.
- Radix UI: Provides a set of unstyled, accessible UI primitives for components like Dialogs, Tooltips, and Dropdowns. This is evident from the dependencies in
package.json. - Tailwind CSS: A utility-first CSS framework used for styling the components. The project's configuration and global styles are defined in
packages/shared/global.css. - class-variance-authority (CVA): Used to define component variants (e.g., different button styles or sizes) in a structured way.
- clsx & tailwind-merge: Utility libraries for conditionally joining class names and intelligently merging Tailwind CSS classes without style conflicts.
Sources: package.json:34-58, packages/shared/components/sheet.tsx:5-10, packages/shared/global.css
The following diagram illustrates how these technologies interact to create a final component.
This diagram shows the build-up of a component, starting from an accessible Radix primitive, which is then styled and given variants to produce the final, reusable React component.
Sources: package.json:48-49, packages/shared/components/sheet.tsx:9,36
Theming
Theming, including support for light and dark modes, is handled through CSS Custom Properties defined within a @layer base in packages/shared/global.css. A :root selector defines the default (light) theme variables, and a .dark class selector overrides these variables for the dark theme. This allows for dynamic theme switching by simply adding or removing the dark class from a parent element.
Sources: packages/shared/global.css:4-70
Theme Variables
The table below lists some of the core variables used for theming.
| Variable | Light Mode Value | Dark Mode Value | Description |
|---|---|---|---|
--background | 0 0% 100% | 222.2 84% 4.9% | The background color of the page. |
--foreground | 222.2 84% 4.9% | 210 40% 98% | The default text color. |
--card | 0 0% 100% | 222.2 84% 4.9% | The background color for card components. |
--primary | 221.2 83.2% 53.3% | 217.2 91.2% 59.8% | The primary accent color for buttons and links. |
--destructive | 0 84.2% 60.2% | 0 62.8% 30.6% | The color for destructive actions (e.g., delete buttons). |
--border | 214.3 31.8% 91.4% | 217.2 32.6% 17.5% | The color for borders and separators. |
--radius | 0.5rem | 0.5rem | The default border radius for components. |
Sources: packages/shared/global.css:5-35, 37-67
Key Components
The library contains several key components that are reused across the application.
Sheet Component
The Sheet component, found in packages/shared/components/sheet.tsx, provides a slide-out panel for displaying content. It is built on top of Radix UI's Dialog primitive (@radix-ui/react-dialog).
It is composed of several sub-components:
Sheet: The root component.SheetTrigger: The element that opens the sheet.SheetContent: The main container for the sheet's content. It uses CVA to support differentsidevariants (top,bottom,left,right).SheetHeader,SheetFooter,SheetTitle,SheetDescription: Semantic components for structuring the content within theSheetContent.SheetClose: A button to close the sheet.
Sources: packages/shared/components/sheet.tsx
The diagram below shows the relationship between the Sheet component parts.
This diagram illustrates how the Sheet component family is structured, with Sheet acting as the root and composing other parts like SheetTrigger and SheetContent.
Sources: packages/shared/components/sheet.tsx:12, 14, 16, 36, 88, 99, 110, 121
Card Component
The Card component is used extensively, for example in packages/web/src/components/page-card.tsx, to display previews of archived pages. It is composed of several parts to structure its content:
Card: The main container.CardHeader: Contains theCardTitleandCardDescription.CardContent: The main body of the card, often containing a screenshot and description.CardFooter: Typically holds action buttons.
Sources: packages/web/src/components/page-card.tsx:9, 51, 53, 59, 67
Tooltip Component
Tooltips are used to provide additional information on hover. They are implemented using Radix UI's Tooltip primitive. The main parts are:
TooltipProvider: Wraps the application to provide context for tooltips.Tooltip: The root tooltip component.TooltipTrigger: The element that triggers the tooltip on hover.TooltipContent: The content that appears in the tooltip.
This component is used in both the web application's PageCard and the browser extension's HistoryTaskList.
Sources: packages/web/src/components/page-card.tsx:73-82, packages/plugin/popup/components/HistoryTaskList.tsx:44-57
Component Usage Examples
The primitive shared components are composed to build more complex, application-specific UI.
PageCard Component
The PageCard component is a prime example of composition. It combines Card, BadgeSpan, ScreenshotView, Button, and Tooltip to create a rich preview of a saved web page.
Sources: packages/web/src/components/page-card.tsx
The following diagram shows the composition of the PageCard.
This diagram shows how various shared components are assembled within the PageCard component to display page information and actions.
Sources: packages/web/src/components/page-card.tsx:50-141
Page Content Rendering
A common pattern in the application is to fetch saved page content as HTML and render it. Two methods are employed.
Iframe Rendering
In both the main archive view and the showcase view, page content is fetched from an API endpoint (e.g., /api/pages/content). The resulting HTML text is converted into a Blob, and an object URL is created using URL.createObjectURL(). This URL is then used as the src for an <iframe>, sandboxing the content for security.
Sources: packages/web/src/pages/(layout)/page.[slug].tsx:17-27, 60-66, packages/web/src/pages/showcase/page.[slug].tsx:10-20, 60-65
This data flow is visualized below.
This sequence diagram details the steps to securely load and display archived page content within an iframe.
Sources: packages/web/src/pages/(layout)/page.[slug].tsx:60-66, packages/web/src/pages/showcase/page.[slug].tsx:60-65
Readability Mode
For a cleaner, article-focused view, the application uses Mozilla's @mozilla/readability library. The ReadabilityPageContent component takes the raw page HTML, parses it to extract the main article content, and then renders this sanitized content directly into the DOM. This provides a "reader mode" experience.
Sources: packages/web/src/components/readability-page-content.tsx:8-11, packages/web/src/pages/(layout)/page.[slug].tsx:88-89
Conclusion
The shared UI component library in packages/shared is a critical part of the web-archive project. By leveraging industry-standard tools like Radix UI and Tailwind CSS, it provides a consistent, themeable, and reusable set of components. This foundation enables efficient development and ensures a high-quality user experience across both the web application and the browser extension.