Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/dev-os/llms.txt

Use this file to discover all available pages before exploring further.

The Articles app presents blog posts the way a file manager presents documents — each post is a large FileText icon with a title and date underneath. Clicking any icon swaps the grid for a focused, paper-white reader view. A single piece of useState — the selected article’s id, or null — drives the entire two-view toggle with no router required.

Data Structure

All articles live in a static array defined at the top of ArticlesApp.js:
const articles = [
  {
    id: 1,
    title: 'Why I stopped using UI Libraries',
    date: '2024-02-15',
    content: 'Building your own components is painful, but ultimately rewarding. Here is a 5000 word essay on why you should reinvent the wheel...',
  },
  {
    id: 2,
    title: 'CSS Grid is magic, actually',
    date: '2023-11-02',
    content: 'Remember floats? Remember clearfixes? If you do, you should be using CSS Grid for everything now. Let me explain...',
  },
  {
    id: 3,
    title: 'The state of React in 2024',
    date: '2024-01-10',
    content: 'Server components, client components, actions, oh my. A brief look at how complicated we made rendering HTML.',
  },
];
Each object requires four fields:
FieldTypePurpose
idnumberUnique key; stored in selectedId state on click
titlestringDisplayed below the icon in the grid and as <h1> in the reader
datestringISO date string shown in the grid tile and reader subtitle
contentstringFull article text rendered in the reader body

Grid View

The grid view is the default state (selectedId === null). It renders when no article is selected. Menu bar — a thin bg-gray-100 border-b border-gray-300 strip at the top of the app contains two <span> elements: File and View. They have hover styles (hover:bg-gray-200) but are not wired to any actions in the current implementation. Icon grid — a CSS grid inside the scrollable body:
<div className="grid grid-cols-2 sm:grid-cols-3 gap-4">
  {articles.map(article => (
    <div
      key={article.id}
      onClick={() => setSelectedId(article.id)}
      className="flex flex-col items-center gap-2 p-4
                 hover:bg-os-teal/10 rounded cursor-pointer
                 border border-transparent hover:border-os-teal/30
                 transition-colors text-center"
    >
      <FileText size={48} className="text-os-teal/80" strokeWidth={1} />
      <div>
        <div className="font-medium text-sm line-clamp-2">{article.title}</div>
        <div className="text-xs text-gray-500 mt-1">{article.date}</div>
      </div>
    </div>
  ))}
</div>
Each tile has a subtle teal tint on hover (bg-os-teal/10) and a matching border that appears on hover. On mobile the grid is 2 columns; from sm: breakpoint upward it expands to 3 columns.

Reader View

Clicking a file icon sets selectedId to the article’s id. The component uses articles.find(a => a.id === selectedId) to resolve the full article object, then renders the reader: Back barbg-gray-100 border-b border-gray-300 font-ui; contains a ← Back to list button that calls setSelectedId(null), returning to the grid. Article bodybg-[#FDFBF7] font-serif, centred with max-w-2xl mx-auto and padded with p-8:
<h1 className="text-3xl font-bold mb-2 text-gray-900">{article.title}</h1>
<p className="text-sm text-gray-500 mb-8 font-ui">{article.date}</p>
<div className="text-lg leading-relaxed text-gray-800">{article.content}</div>
The warm off-white background (#FDFBF7) and font-serif body text give the reader a book-like feel distinct from the rest of the OS chrome.

Customising the Articles App

Add a new article — append an object to the articles array. Use a template literal for content if you want line breaks:
{
  id: 4,
  title: 'Why TypeScript makes me feel things',
  date: '2024-06-01',
  content: `Paragraph one here.\n\nParagraph two here.`,
},
Remove an article — delete its object from the array. The grid reflows automatically. Change the iconFileText is imported from lucide-react. Swap it for any other Lucide icon (e.g. BookOpen, FileCode) to match your content type.
The reader renders content as a plain string using a text node — whitespace characters like \n are collapsed by the browser. To support Markdown formatting or rich HTML, replace the content <div> with a lightweight parser such as react-markdown or marked:
import ReactMarkdown from 'react-markdown';
// ...
<ReactMarkdown>{article.content}</ReactMarkdown>

Build docs developers (and LLMs) love