Skip to main content

Posts

Overview

Posts are the primary content unit in CommonPlace. Unlike conventional social media posts, every CommonPlace post must carry an explicit intent signal before it can be published, ensuring readers always have context about how content should be received. Posts support multiple editor types (simple text, longform articles, drawings, and magnetic poetry), voice narration, multiple images, audience controls, and rich visual customization tied to the author's intent styles.

The post viewing experience renders posts as styled cards within a masonry grid. Each card displays an intent badge overlapping the author's avatar, the author's name and timestamp, the post content, any attached media, and action buttons for responding, sharing, saving, and more. Posts respect audience settings (public, friends-only, or circle-specific) and can be shared to circles, "built upon" with quoted excerpts, or reported.

Post editing is supported inline, and all posts include an edited_at timestamp when modified. Post deletion is also available to the author. Content longer than 500 non-space characters is truncated in the feed view with a "read more" indicator that navigates to the full post detail page.

Relevant Invariants

  • Invariant #4: "Intent Precedes Interpretation" -- The composer requires intent selection before content entry.
  • Invariant #5: "Repair Over Punishment" -- Editing is a normal action; edited posts show a subtle "(edited)" indicator rather than punishing the author.
  • Invariant #1: "Participation Is Always Voluntary" -- The composer uses no prompts like "What's on your mind?"; the placeholder is empty.

User Experience

Creating a Post (Quick Composer)

  1. User opens the composer from the feed header.
  2. The intent selection overlay appears with the prompt "How should this be received?" and six intent pills.
  3. User selects an intent. The overlay transforms into a styled compose frame matching the intent's color scheme.
  4. User writes content in the textarea (no placeholder text).
  5. Optionally: attach up to 10 images (JPG, PNG, GIF, WebP; max 5MB each), record a voice note, select audience (public/friends/circles), and toggle sharing permission.
  6. A link to "Open full editor" navigates to the Advanced Composer with current state transferred.
  7. User clicks "Post" to publish. The button color matches the selected intent's accent color.

Viewing a Post

  1. Posts render as cards in a masonry grid with the author's intent-specific styling (font, surface, frame, effects, emphasis).
  2. The IntentBadge in the corner shows the author's avatar with the intent emoji overlapping it.
  3. PostHeader shows the author name (linking to their HomeRoom), timestamp, edit indicator, voice indicator, and audience badge.
  4. PostContent renders text content with truncation at 500 non-space characters. Users can select text to "Quote this" for building on the post.
  5. PostImages renders attached images in a responsive grid.
  6. PostActions provides buttons: Reply, Edit (author only), Delete (author only), Copy Link, Share to Circle, Build On, Report (non-author), and Save with shelf options.
  7. Clicking anywhere on the post card navigates to the post detail page at /post/:id.

Technical Implementation

Key Files

FilePurpose
src/components/Post.jsxMain post card component (1037 lines). Handles rendering, editing, saving, sharing, reporting, and Build On
src/components/CreatePost.jsxQuick composer with intent-first flow (623 lines)
src/components/post-parts/IntentBadge.jsxCorner avatar + intent indicator overlay
src/components/post-parts/PostHeader.jsxAuthor name, timestamp, edit indicator, audience badge
src/components/post-parts/PostContent.jsxText content with truncation and quote selection
src/components/post-parts/PostImages.jsxResponsive image grid
src/components/post-parts/PostActions.jsxAction button bar (Reply, Edit, Delete, Share, Build On, Save, Report)
src/components/post-parts/SavePopover.jsxPortal-based save options popover with shelf selection and notes

Post Card Styling

Posts are styled dynamically based on the author's intent style configuration:

  • CSS classes: post-surface-{light|dark|warm|cool}, post-frame-{flat|paper|soft-card|ghost}, post-effects-{none|...}, post-emphasis-{subtle|normal|strong}
  • CSS custom properties: --intent-tint, --intent-accent, --intent-border, --intent-content, --author-font, --background-image
  • Default style: System font, flat frame, light surface, no effects, normal emphasis

Editor Types

The post system supports four editor types (via Advanced Composer):

Editoreditor_type valueContent Storage
Simple Textsimplecontent field as plain text
Longform Articlelongformcontent field as HTML, title field
DrawingdrawingImage uploaded to storage, content set to "[Drawing]"
Magnetic Poetrymagnetic_poetrystructured_content JSON with words and positions

Database Tables

TablePurpose
postsCore post data: content, intent, audience, editor_type, title, structured_content, post_style, voice_url, voice_duration_ms, voice_mime, external_clip_id, builds_on_post_id, quoted_excerpt, is_share, shared_post_id, share_note. Note: sharing_allowed and content_note are referenced in frontend code but may not yet exist in the database (see CLAUDE.md known bugs).
post_imagesMultiple images per post with position ordering
post_circlesAssociates posts with circles for circle-audience posts
commentsResponses to posts (displayed with count on post card)
profilesAuthor display info (fetched via join)

Edge Cases

ScenarioBehavior
No intent selectedPost button is disabled
Empty content with no mediaPost button is disabled
Circles audience with no circles selectedPost button is disabled
Post content exceeds 500 non-space charsTruncated in feed with "..." and arrow indicator; full content on detail page
Author edits postInline textarea appears; edited_at timestamp saved; "(edited)" indicator shown
Post has voice narrationVolume icon in header; VoicePlayer rendered with preload="none"
Post is a shareRenders with SharedPostCard component instead of standard layout; "Shares cannot be re-shared" footer
Post has builds_on_post_idShows "Building on:" lineage with OriginalPostCard; handles deleted originals gracefully
External clip attachedExternalContentCard rendered within post content

Last updated: 2026-02-07