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)
- User opens the composer from the feed header.
- The intent selection overlay appears with the prompt "How should this be received?" and six intent pills.
- User selects an intent. The overlay transforms into a styled compose frame matching the intent's color scheme.
- User writes content in the textarea (no placeholder text).
- 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.
- A link to "Open full editor" navigates to the Advanced Composer with current state transferred.
- User clicks "Post" to publish. The button color matches the selected intent's accent color.
Viewing a Post
- Posts render as cards in a masonry grid with the author's intent-specific styling (font, surface, frame, effects, emphasis).
- The IntentBadge in the corner shows the author's avatar with the intent emoji overlapping it.
- PostHeader shows the author name (linking to their HomeRoom), timestamp, edit indicator, voice indicator, and audience badge.
- PostContent renders text content with truncation at 500 non-space characters. Users can select text to "Quote this" for building on the post.
- PostImages renders attached images in a responsive grid.
- 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.
- Clicking anywhere on the post card navigates to the post detail page at
/post/:id.
Technical Implementation
Key Files
| File | Purpose |
|---|---|
src/components/Post.jsx | Main post card component (1037 lines). Handles rendering, editing, saving, sharing, reporting, and Build On |
src/components/CreatePost.jsx | Quick composer with intent-first flow (623 lines) |
src/components/post-parts/IntentBadge.jsx | Corner avatar + intent indicator overlay |
src/components/post-parts/PostHeader.jsx | Author name, timestamp, edit indicator, audience badge |
src/components/post-parts/PostContent.jsx | Text content with truncation and quote selection |
src/components/post-parts/PostImages.jsx | Responsive image grid |
src/components/post-parts/PostActions.jsx | Action button bar (Reply, Edit, Delete, Share, Build On, Save, Report) |
src/components/post-parts/SavePopover.jsx | Portal-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):
| Editor | editor_type value | Content Storage |
|---|---|---|
| Simple Text | simple | content field as plain text |
| Longform Article | longform | content field as HTML, title field |
| Drawing | drawing | Image uploaded to storage, content set to "[Drawing]" |
| Magnetic Poetry | magnetic_poetry | structured_content JSON with words and positions |
Database Tables
| Table | Purpose |
|---|---|
posts | Core 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_images | Multiple images per post with position ordering |
post_circles | Associates posts with circles for circle-audience posts |
comments | Responses to posts (displayed with count on post card) |
profiles | Author display info (fetched via join) |
Edge Cases
| Scenario | Behavior |
|---|---|
| No intent selected | Post button is disabled |
| Empty content with no media | Post button is disabled |
| Circles audience with no circles selected | Post button is disabled |
| Post content exceeds 500 non-space chars | Truncated in feed with "..." and arrow indicator; full content on detail page |
| Author edits post | Inline textarea appears; edited_at timestamp saved; "(edited)" indicator shown |
| Post has voice narration | Volume icon in header; VoicePlayer rendered with preload="none" |
| Post is a share | Renders with SharedPostCard component instead of standard layout; "Shares cannot be re-shared" footer |
| Post has builds_on_post_id | Shows "Building on:" lineage with OriginalPostCard; handles deleted originals gracefully |
| External clip attached | ExternalContentCard rendered within post content |
Related
- Intent System -- Intent selection is the first step of post creation
- Advanced Composer -- Full-featured post creation with multiple editor types
- Feed -- Posts are rendered within the feed
- Saved & Shelves -- Posts can be saved to shelves
- Voice Narration -- Voice notes can be attached to posts
Last updated: 2026-02-07