🌌 Refactor Schedule Event Rendering
Segmented Buffers + Main-Only Resize + Hierarchical Glow (Hardened)
🎯 Purpose
This PR refactors the Schedule event rendering architecture to:
- Render prep & travel as proportional internal segments
- Enforce strict side-by-side overlap (no stacking)
- Apply glow only at wrapper level
- Restrict resizing to main duration only
- Keep prep & travel static during drag
- Maintain proportional rendering (no artificial minimum heights)
- Eliminate hardcoded values (all constants must use design tokens)
- Remove motion/animation drift
This establishes the structural foundation of the Cosmic Laboratory model:
Time is spatially honest. Buffers are structural. Glow is restrained.
🧠 Canonical Data Model
🔒 Source of Truth (Non-Negotiable)
mainStart and mainEnd are canonical.
prepDuration and travelDuration are canonical.
renderStart and renderEnd are always derived.
event.start MUST equal renderStart.
event.end MUST equal renderEnd.
- No logic may derive main duration from
event.start.
If this rule is violated, resize logic will regress.
📦 Event Structure
extendedProps: {
mainStart: Date,
mainEnd: Date,
prepDuration: number, // minutes
travelDuration: number // minutes
}
🧮 Derived Values
const totalBuffer = prepDuration + travelDuration
const renderStart = new Date(mainStart.getTime() - totalBuffer * 60000)
const renderEnd = mainEnd
🧱 Event Rendering Architecture
Single FullCalendar Event
Only ONE FC event is rendered per logical event.
It spans:
Internal segmentation handles visual subdivision.
Internal Structure
<div className="fc-event-wrapper">
{travelDuration > 0 && (
<div
className="event-segment event-travel"
style={{ height: `${travelRatio}%` }}
/>
)}
{prepDuration > 0 && (
<div
className="event-segment event-prep"
style={{ height: `${prepRatio}%` }}
/>
)}
<div
className="event-segment event-main"
style={{ height: `${mainRatio}%` }}
>
{title}
</div>
</div>
Strict Proportional Ratios
const mainDuration = Math.max(
1,
(mainEnd.getTime() - mainStart.getTime()) / 60000
)
const totalDuration = prepDuration + travelDuration + mainDuration
const travelRatio = (travelDuration / totalDuration) * 100
const prepRatio = (prepDuration / totalDuration) * 100
const mainRatio = (mainDuration / totalDuration) * 100
⚠️ Constraints
- NO minimum height
- NO clamping
- NO artificial padding
- Segments may render below 3px — this is acceptable
- Do NOT add fallback UI for small durations
🎨 Styling Constraints (Token Enforcement)
🚫 ABSOLUTE RULE
No hardcoded values are allowed in CSS.
Forbidden:
- Inline RGBA
- Inline px values
- Inline border-radius
- Inline blur
- Inline shadow
- Inline spacing
Required:
All values must use tokens from:
tokens.css
base.css
- Design system variables
Wrapper Styling (Glow Applied Once)
.fc-event-wrapper {
border-radius: var(--radius-lg);
overflow: hidden;
backdrop-filter: var(--event-blur);
background:
var(--event-glass-gradient),
var(--event-base-surface);
border: 1px solid var(--event-border);
box-shadow: var(--event-shadow);
}
Glow must exist as a token:
--event-shadow:
var(--shadow-elevation-md),
var(--shadow-glow-soft);
Segment Styling
.event-segment {
width: 100%;
}
.event-travel {
background: var(--event-travel-surface);
opacity: var(--event-travel-opacity);
border-bottom: 1px solid var(--event-divider);
}
.event-prep {
background: var(--event-prep-surface);
opacity: var(--event-prep-opacity);
border-bottom: 1px solid var(--event-divider);
}
.event-main {
position: relative;
}
⚠️ Prohibited
- No glow on segments
- No dashed borders
- No gradient stacking
- No additional shadow layers
Glow belongs to wrapper only.
🔄 Resize Behaviour (Main Only)
Resize Detection
const resizedFromTop =
info.startDelta && !info.endDelta
const resizedFromBottom =
info.endDelta && !info.startDelta
Resize Logic
function handleEventResize(info) {
const { event } = info
let mainStart = event.extendedProps.mainStart
let mainEnd = event.extendedProps.mainEnd
if (resizedFromTop) {
mainStart = newDate
}
if (resizedFromBottom) {
mainEnd = newDate
}
const prep = event.extendedProps.prepDuration
const travel = event.extendedProps.travelDuration
const renderStart =
new Date(mainStart.getTime() - (prep + travel) * 60000)
const renderEnd = mainEnd
event.setDates(renderStart, renderEnd)
event.setExtendedProp('mainStart', mainStart)
event.setExtendedProp('mainEnd', mainEnd)
}
Resize Constraints
- Prep/travel durations NEVER change during resize
- Dragging entire event moves whole block
- No dynamic recalculation
- No contextual inference
- After resize, clustering must re-run
↔ Strict Side-by-Side Overlap
Column Layout
const gap = parseFloat(
getComputedStyle(document.documentElement)
.getPropertyValue('--event-column-gap')
)
const width = `calc((100% - ${(totalColumns - 1) * gap}px) / ${totalColumns})`
style={{
left: `calc(${columnIndex} * (${width} + ${gap}px))`,
width
}}
Overlap Rules
- No vertical stacking
- No z-index escalation
- No hover elevation over sibling columns
- Equal width distribution
🚫 Motion & Animation Constraints
The schedule is an instrument panel, not a marketing site.
Prohibited:
- No scale transforms
- No hover lift transforms
- No brightness filters
- No animated glow
- No gradient shimmer
- No motion beyond opacity/shadow ≤ 250ms
🧪 Edge Case Handling
Very Small Durations
Segments may render below 3px.
Do NOT substitute UI.
Do NOT pad.
Zero Buffers
If prep or travel = 0:
- Do not render segment
- Do not render divider
Zero Main Duration
Guard:
mainDuration = Math.max(1, computedMainDuration)
Synchronisation Rule
After any resize or drag:
mainStart
mainEnd
renderStart
renderEnd
must be synchronised.
🚫 Explicit Non-Goals
This PR must NOT:
- Modify buttons
- Modify modals
- Modify toolbar
- Introduce gradients
- Introduce new animations
- Introduce new hardcoded colors
UI harmonisation is a separate PR.
🧠 Design Principles Enforced
- Time is spatially honest
- Buffers are structural
- Glow is restrained
- Motion is minimal
- No artificial amplification
- All constants use tokens
- Predictability > decoration
🌌 Refactor Schedule Event Rendering
Segmented Buffers + Main-Only Resize + Hierarchical Glow (Hardened)
🎯 Purpose
This PR refactors the Schedule event rendering architecture to:
This establishes the structural foundation of the Cosmic Laboratory model:
🧠 Canonical Data Model
🔒 Source of Truth (Non-Negotiable)
mainStartandmainEndare canonical.prepDurationandtravelDurationare canonical.renderStartandrenderEndare always derived.event.startMUST equalrenderStart.event.endMUST equalrenderEnd.event.start.If this rule is violated, resize logic will regress.
📦 Event Structure
🧮 Derived Values
🧱 Event Rendering Architecture
Single FullCalendar Event
Only ONE FC event is rendered per logical event.
It spans:
Internal segmentation handles visual subdivision.
Internal Structure
Strict Proportional Ratios
🎨 Styling Constraints (Token Enforcement)
🚫 ABSOLUTE RULE
No hardcoded values are allowed in CSS.
Forbidden:
Required:
All values must use tokens from:
tokens.cssbase.cssWrapper Styling (Glow Applied Once)
Glow must exist as a token:
Segment Styling
Glow belongs to wrapper only.
🔄 Resize Behaviour (Main Only)
Resize Detection
Resize Logic
Resize Constraints
↔ Strict Side-by-Side Overlap
Column Layout
Overlap Rules
🚫 Motion & Animation Constraints
The schedule is an instrument panel, not a marketing site.
Prohibited:
🧪 Edge Case Handling
Very Small Durations
Segments may render below 3px.
Do NOT substitute UI.
Do NOT pad.
Zero Buffers
If prep or travel = 0:
Zero Main Duration
Guard:
Synchronisation Rule
After any resize or drag:
mainStartmainEndrenderStartrenderEndmust be synchronised.
🚫 Explicit Non-Goals
This PR must NOT:
UI harmonisation is a separate PR.
🧠 Design Principles Enforced