-
Notifications
You must be signed in to change notification settings - Fork 0
Closed
Description
Summary
We've been experimenting with a new cell layout in runt notebook that creates a cleaner, more Colab-like "paper" aesthetic. The key innovation is a gutter ribbon system - a continuous vertical ribbon on the left side of the notebook that changes color by cell type.
Design Rationale
The Problem
Traditional notebook UIs often use heavy borders, cards, or box shadows to delineate cells. This creates visual clutter and a "boxy" feel that interrupts the flow of reading/writing.
The Solution: Gutter Ribbon
A two-part gutter on the left side of each cell:
- Action area (24px / w-6) - Space for contextual actions like play buttons
- Thin ribbon (4px / w-1) - Colored indicator of cell type
This creates a continuous "spine" down the notebook that:
- Provides visual hierarchy without heavy borders
- Shows cell type at a glance (scan the ribbon colors)
- Maintains a clean "paper" aesthetic
- Offers an obvious home for cell actions
Color System
- Code cells: Gray (unfocused: gray-200, focused: gray-400)
- Markdown cells: Currently amber, but considering light blue (unfocused: light, focused: darker)
- Focus background: Subtle tinted background (gray-50/50 or amber-50/50 at 50% opacity)
Code Changes
CellContainer.tsx
import { forwardRef, type ReactNode } from "react";
import { cn } from "@/lib/utils";
interface CellContainerProps {
id: string;
cellType?: "code" | "markdown";
isFocused?: boolean;
onFocus?: () => void;
children: ReactNode;
/** Content to render in the gutter (e.g., play button) */
gutterContent?: ReactNode;
onDragStart?: (e: React.DragEvent) => void;
onDragOver?: (e: React.DragEvent) => void;
onDrop?: (e: React.DragEvent) => void;
className?: string;
}
const getGutterColor = (cellType?: "code" | "markdown", isFocused?: boolean) => {
switch (cellType) {
case "markdown":
return isFocused ? "bg-amber-400" : "bg-amber-200";
case "code":
default:
return isFocused ? "bg-gray-400" : "bg-gray-200";
}
};
const getFocusBgColor = (cellType?: "code" | "markdown") => {
switch (cellType) {
case "markdown":
return "bg-amber-50/50";
case "code":
default:
return "bg-gray-50/50";
}
};
export const CellContainer = forwardRef<HTMLDivElement, CellContainerProps>(
(
{
id,
cellType,
isFocused = false,
onFocus,
children,
gutterContent,
onDragStart,
onDragOver,
onDrop,
className,
},
ref,
) => {
const gutterColor = getGutterColor(cellType, isFocused);
const focusBgColor = getFocusBgColor(cellType);
return (
<div
ref={ref}
data-slot="cell-container"
data-cell-id={id}
className={cn(
"cell-container group flex transition-colors duration-150",
isFocused && focusBgColor,
className,
)}
onMouseDown={onFocus}
draggable={!!onDragStart}
onDragStart={onDragStart}
onDragOver={onDragOver}
onDrop={onDrop}
>
{/* Gutter area: action button + thin ribbon */}
<div className="flex-shrink-0 flex">
{/* Action button area (play button for code cells) */}
<div className="w-6 flex items-start justify-center pt-1.5">
{gutterContent}
</div>
{/* Thin ribbon */}
<div
className={cn(
"w-1 transition-colors duration-150",
gutterColor,
)}
/>
</div>
{/* Cell content */}
<div className="flex-1 min-w-0">
{children}
</div>
</div>
);
},
);
CellContainer.displayName = "CellContainer";PlayButton.tsx - Smart Visibility
The play button should be invisible when unfocused, visible on focus or cell hover:
className={cn(
"flex items-center justify-center transition-all",
isRunning
? "text-destructive hover:text-destructive animate-pulse"
: isFocused
? focusedClass // e.g., "text-gray-700"
: "text-transparent group-hover:text-muted-foreground hover:text-foreground",
isAutoLaunching && "cursor-wait opacity-75",
className,
)}Usage Example
<CellContainer
id={cell.id}
cellType="code"
isFocused={isFocused}
onFocus={onFocus}
gutterContent={<PlayButton ... />}
>
{/* Cell content */}
</CellContainer>Open Questions
- Markdown color: Amber works but might be too "warning-like". Light blue (sky/cyan) could be a good alternative that feels more neutral/documentation-like.
- Raw cells: What color should raw cells use? Perhaps a neutral slate?
- Custom cell types: Should the color system be extensible for custom cell types?
Reference
See the implementation in action: runtimed/runt#18
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels