Skip to main content

Documentation Index

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

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

The Projects page reimagines a traditional portfolio grid as a living solar system. A white glowing “sun” anchors the centre of the viewport; three project payloads orbit it at different radii and speeds using Framer Motion’s animate rotation. Each payload is a hoverable circle. Moving the cursor over a project opens a telemetry panel — a glass-morphism card that slides in with the project name, description, tech stack tags, and action buttons.

Visual Overview

ElementDescription
Central starWhite bg-star-white circle with a pulsing box-shadow glow
Orbit pathsThree circular <div> rings at diameters 250 px, 380 px, and 500 px
Project nodesGradient circles travelling along each orbit via Framer Motion rotate
Telemetry panelConditionally rendered glass-panel card shown on hover
TeletypeText"Tracking active payloads in sector..." — page-level intro line
The three orbits are deliberately spaced so they never visually overlap, and each payload moves at a different angular velocity, ensuring the orrery always looks dynamic even when no project is selected.

Project Payloads Data

The three projects are defined in the pt array inside the gt component:
const projects = [
  {
    id: 1,
    name: 'Nebula UI',
    desc: 'A cosmic-themed component library built with React and Tailwind.',
    tech: ['React', 'Tailwind', 'Framer'],
    color: 'from-aurora-teal to-blue-900',
    orbitSize: 250,
    speed: 20,
  },
  {
    id: 2,
    name: 'Pulsar Analytics',
    desc: 'Real-time dashboard for tracking high-frequency data streams.',
    tech: ['TypeScript', 'D3.js', 'WebSocket'],
    color: 'from-aurora-magenta to-purple-900',
    orbitSize: 380,
    speed: 35,
  },
  {
    id: 3,
    name: 'Void Commerce',
    desc: 'Headless e-commerce storefront with sub-second page loads.',
    tech: ['Next.js', 'Shopify API', 'GraphQL'],
    color: 'from-aurora-green to-emerald-900',
    orbitSize: 500,
    speed: 50,
  },
]

Project Properties Reference

PropertyTypeDescription
idnumberUnique identifier; used as the React key and to track the active project in state
namestringDisplayed in the telemetry panel heading
descstringShort description shown in the telemetry panel body
techstring[]Tech tags rendered as pill badges in the telemetry panel
colorstringTailwind gradient classes applied to the project node circle
orbitSizenumber (px)Diameter of the orbit ring in pixels; larger = further from the sun
speednumber (s)Duration of one full orbit in seconds; larger = slower revolution

Orbital Animation

Each project node is placed at the top of its orbit ring using absolute positioning, and the ring wrapper rotates continuously via Framer Motion:
// Orbit ring wrapper — rotates continuously via Framer Motion
<motion.div
  className="w-full h-full absolute"
  animate={{ rotate: 360 }}
  transition={{ duration: project.speed, repeat: Infinity, ease: 'linear' }}
>
  {/* Project node — pinned to top of ring */}
  <div className="absolute top-0 left-1/2 -translate-x-1/2 -translate-y-1/2">
    <div
      className={`w-12 h-12 rounded-full bg-gradient-to-br ${project.color}
                  shadow-[0_0_20px_currentColor] cursor-pointer
                  transition-transform hover:scale-125`}
    />
  </div>
</motion.div>
Because the node is a child of the rotating wrapper, it travels with the ring. The node itself is not counter-rotated, so its gradient orientation shifts as it orbits.

Telemetry Panel

Hovering a project node sets activeProject in component state (onMouseEnter / onMouseLeave). The telemetry panel is rendered conditionally at the bottom-right of the viewport:
{activeProject ? (
  <motion.div
    initial={{ opacity: 0, y: 20 }}
    animate={{ opacity: 1, y: 0 }}
    className="glass-panel p-6 border-aurora-teal/40"
  >
    <h3 className="text-2xl font-heading font-bold text-star-white mb-2">
      {activeProject.name}
    </h3>
    <p className="text-star-dim text-sm mb-4">{activeProject.desc}</p>

    {/* Tech stack tags */}
    <div className="flex flex-wrap gap-2 mb-6">
      {activeProject.tech.map(tag => (
        <span key={tag} className="text-xs font-mono px-2 py-1 bg-cosmic-navy rounded text-aurora-teal">
          {tag}
        </span>
      ))}
    </div>

    {/* Action buttons */}
    <div className="flex gap-4">
      <button className="flex items-center gap-2 text-sm hover:text-aurora-teal transition-colors interactive">
        Launch
      </button>
      <button className="flex items-center gap-2 text-sm hover:text-aurora-teal transition-colors interactive">
        Source
      </button>
    </div>
  </motion.div>
) : (
  <div className="glass-panel p-6 border-star-dim/20 text-center text-star-dim font-mono text-sm">
    Hover over a payload to intercept telemetry data.
  </div>
)}
When no project is hovered, the panel shows a placeholder prompt. Moving the cursor off a node (onMouseLeave) clears activeProject and restores the placeholder.

TeletypeText Configuration

<TeletypeText
  text="Tracking active payloads in sector..."
  className="text-aurora-teal font-mono text-sm"
/>

Customization

  1. Add a project — Append a new object to the projects array. Choose an orbitSize larger than 500 (e.g., 620) and a proportionally slower speed (e.g., 65). The orbit ring and node will render automatically.
  2. Remove a project — Delete its entry from projects. The remaining orbits stay at their configured radii; no layout code needs changing.
  3. Edit project descriptions — Update the desc string for each project. Keep descriptions concise (1–2 sentences) so the telemetry panel does not overflow on small viewports.
  4. Update tech tags — Modify the tech array for each project. There is no hard limit on tag count, but 3–5 tags keeps the panel tidy.
  5. Change orbit spacing — Adjust orbitSize values. A minimum gap of ~100 px between diameters prevents visual crowding.
  6. Change orbit speed — Adjust speed values. Lower = faster revolution. The current 20s → 35s → 50s progression gives a natural feel where outer orbits move more slowly.
  7. Change the gradient — Update the color string with any two Tailwind gradient color stops to restyle a project node.
On mobile, the 500 px outer orbit extends beyond most viewport widths. Consider switching to a vertical list layout below a breakpoint using a Tailwind responsive prefix: wrap the orbital canvas in a hidden md:block div and add a mobile-only block md:hidden project card list below it.
The orbit rings are plain <div> elements with border and matching width/height values — not SVG. If you change an orbitSize, only the single style object on that ring’s wrapper div needs updating; there are no separate SVG elements to keep in sync.

Build docs developers (and LLMs) love