Skip to content

RodrigoRivasCo/gridelve

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

A professional and aesthetic 12-column grid system for React applications. Inspired by the design system of hex.tech.


Quick Start & Installation

Dependencies

npm install styled-components polished motion utopia-core
Package Purpose
styled-components CSS-in-JS styling engine
polished Color manipulation utilities
motion Framer Motion animations
utopia-core Fluid typography calculations

Basic Setup

Wrap your application in the GridelveThemeProvider and inject GlobalStyle at the root:

// _app.tsx or layout.tsx
import {
  GridelveThemeProvider,
  GlobalStyle,
  LayoutGrid,
  LayoutSubgrid,
} from "./gridelve/src";

export default function App({ children }) {
  return (
    <GridelveThemeProvider>
      <GlobalStyle />
      <LayoutGrid columns={12}>
        <LayoutSubgrid>{children}</LayoutSubgrid>
      </LayoutGrid>
    </GridelveThemeProvider>
  );
}

The "Headless" Theme System

The engine uses CSS custom properties (variables) as its API. Override these in your :root to customize the entire system.

CSS Variable Reference

Variable Default Function
--ge-max-width-grid 1840px Maximum width of the 12-column content zone
--ge-max-width-grid-condensed 1536px Max-width for LayoutGridCondensed
--ge-max-width-page 1240px Max-width for inner content in BasicPageRow
--ge-grid-major #3a3a4a Color for outer borders and primary grid lines
--ge-grid-minor #2a2a3a Color for internal guidelines and subtle lines
--ge-bg-primary #14141c Main page background
--ge-bg-secondary #1a1a24 Alternate section background
--ge-highlight-color #f5c0c0 Accent color for focus states and selections
--ge-highlight-color-05 5% opacity Subtle highlight (CartesianGrid background)
--ge-highlight-color-20 20% opacity Stronger highlight (CartesianGrid gradient)
--ge-font-sans 'Inter', sans-serif Default body font
--ge-font-serif 'Playfair Display', serif Heading font slot
--ge-font-mono 'JetBrains Mono', monospace Code/technical font

Customizing via CSS

Override in your global styles:

:root {
  --ge-grid-major: #your-brand-color;
  --ge-highlight-color: #your-accent-color;
  --ge-font-sans: "Your Font", sans-serif;
}

Customizing via TypeScript

Extend the theme interface in theme/types.ts:

import { GridelveTheme } from "./types";

const myTheme: GridelveTheme = {
  name: "custom",
  gridColor: {
    major: "#custom-major",
    minor: "#custom-minor",
  },
  highlightColor: "#custom-highlight",
  highlightColor05: "color-mix(in srgb, #custom-highlight 5%, transparent)",
  highlightColor20: "color-mix(in srgb, #custom-highlight 20%, transparent)",
  backgroundColor: "#custom-bg",
};

Core Components Guide

LayoutGrid

The foundational 12-column grid.

The Math:

grid-template-columns: 1fr var(--content) 1fr;

This creates three tracks:

  1. Left margin (1fr): Flexible space that pushes content to center
  2. Content zone (--content): 12 equal columns, max 1840px
  3. Right margin (1fr): Mirrors left margin

Props:

Prop Type Default Description
columns number 12 Number of columns in the content zone

Usage:

import { LayoutGrid, LayoutSubgrid } from "gridelve";

<LayoutGrid columns={12}>
  <LayoutSubgrid>
    <div style={{ gridColumn: "1 / span 6" }}>Left Half</div>
    <div style={{ gridColumn: "7 / span 6" }}>Right Half</div>
  </LayoutSubgrid>
</LayoutGrid>;

LayoutGridCondensed:

A narrower variant (1536px max) for document-like sections:

import { LayoutGridCondensed } from "gridelve";

<LayoutGridCondensed>
  <p>Narrower content area</p>
</LayoutGridCondensed>;

GridLines & CartesianGrid

Components for the "Blueprint" aesthetic.

GridRule:

A 1px vertical line at a specific column:

import { GridRule, LayoutSubgrid } from "gridelve";

<LayoutSubgrid>
  <GridRule $column={4} /> {/* Line after column 4 */}
  <GridRule $column={8} /> {/* Line after column 8 */}
  <div style={{ gridColumn: "5 / 8" }}>Content between lines</div>
</LayoutSubgrid>;

ContentMarginLine:

Vertical border at the content zone edge:

import { ContentMarginLine } from "gridelve";

<LayoutSubgrid>
  <ContentMarginLine /> {/* Left border */}
  <div>Content</div>
  <ContentMarginLine data-right /> {/* Right border */}
</LayoutSubgrid>;
Attribute Effect
data-right Places line on right edge
data-minor Uses --ge-grid-minor instead of --ge-grid-major
data-top Adds top border
data-bottom Adds bottom border

CheckeredMargin:

The technical "industry marks" pattern:

import { CheckeredMargin } from "gridelve";

<LayoutSubgrid>
  <CheckeredMargin /> {/* Left checkered margin */}
  <div>Content</div>
  <CheckeredMargin data-right /> {/* Right checkered margin */}
</LayoutSubgrid>;

CartesianGrid:

The coordinate background pattern:

import { CartesianGrid } from "gridelve";

<div style={{ position: "relative", minHeight: "400px" }}>
  <CartesianGrid fadeDirection="in" /> {/* Fades from center out */}
  <CartesianGrid fadeDirection="out" /> {/* Fades from edges in */}
</div>;

BasicPageRow

A pre-built section template with optional visual features.

Props:

Prop Type Default Description
topBorder boolean false Adds a --ge-grid-major border at the top
cartesianGrid boolean false Enables the coordinate background
fullWidth boolean false Content spans full width (no max-width constraint)
className string Custom class for the inner container

Usage:

import { BasicPageRow } from 'gridelve';

// Standard section
<BasicPageRow>
  <h2>Section Title</h2>
  <p>Content here</p>
</BasicPageRow>

// Technical section with borders and grid
<BasicPageRow topBorder cartesianGrid>
  <h2>Technical Section</h2>
</BasicPageRow>

// Full-width section (no max-width)
<BasicPageRow fullWidth>
  <img src="hero.jpg" style={{ width: '100%' }} />
</BasicPageRow>

How It Works:

BasicPageRow internally composes:

  1. LayoutSubgrid as the wrapper
  2. CheckeredMargin on left and right edges
  3. LayoutGridCondensed for the content zone
  4. Optional CartesianGrid as background
  5. Animated Inner container with standard padding

Toggling CheckeredMargin:

To remove the checkered aesthetic, create a custom row:

import {
  LayoutSubgrid,
  LayoutGridCondensed,
  ContentMarginLine,
} from "gridelve";
import { motion } from "motion/react";

function CleanPageRow({ children }) {
  return (
    <LayoutSubgrid>
      <ContentMarginLine /> {/* Solid line instead */}
      <LayoutGridCondensed>
        <motion.div>{children}</motion.div>
      </LayoutGridCondensed>
      <ContentMarginLine data-right />
    </LayoutSubgrid>
  );
}

Fluid Utils

Responsive scaling using Utopia calculations.

generateFluidPxScale:

For spacing that scales with viewport:

import { generateFluidPxScale } from "gridelve";

// Returns a CSS clamp() value
const padding = generateFluidPxScale(16, 48);
// → "clamp(16px, calc(16px + (48 - 16) * ((100vw - 450px) / (1250 - 450))), 48px)"

generateFluidFontSize:

For typography that scales with viewport:

import { generateFluidFontSize } from "gridelve";

const h1Size = generateFluidFontSize(32, 64);
// Use in styled-components:
const Heading = styled.h1`
  font-size: ${generateFluidFontSize(32, 64)};
`;

Breakpoints:

Both functions use MEDIA_QUERIES from breakpoints.ts:

Breakpoint Value
MOBILE 450px
SMALL 600px
MEDIUM 800px
LARGE 1000px
EXTRA_LARGE 1250px

Technical Constraints

LayoutSubgrid Requirement

LayoutSubgrid uses grid-template-columns: subgrid, a CSS feature that inherits grid tracks from the parent.

Critical Rule:
LayoutSubgrid MUST be a direct child of LayoutGrid or another LayoutSubgrid.

Why this matters:

// ✅ CORRECT: Direct child of LayoutGrid
<LayoutGrid>
  <LayoutSubgrid>
    <div>Aligned to grid</div>
  </LayoutSubgrid>
</LayoutGrid>

// ❌ BROKEN: Wrapped in a non-grid container
<LayoutGrid>
  <div>                          {/* This breaks subgrid */}
    <LayoutSubgrid>
      <div>Columns will NOT align</div>
    </LayoutSubgrid>
  </div>
</LayoutGrid>

If you wrap LayoutSubgrid in a flexbox, div, or other non-grid container, the subgrid inheritance breaks and columns collapse.

Specificity Fix: :where(& > *)

The grid uses :where() for child selectors:

:where(& > *) {
  grid-column: 2 / -2;
}

Why :where() instead of > *?

  • :where() has zero specificity
  • This allows you to override grid-column on children without fighting the grid's styles
  • If we used > *, you'd need !important to override

Example:

// This works because :where() has zero specificity
<LayoutGrid>
  <div style={{ gridColumn: "1 / -1" }}>
    {" "}
    {/* Full bleed - no conflict */}
    Full width content
  </div>
</LayoutGrid>

The data-pile Attribute

For layered/stacked content on the same grid row:

<LayoutSubgrid>
  <LayoutSubgrid data-pile>
    <div>Background layer</div>
  </LayoutSubgrid>
  <LayoutSubgrid data-pile>
    <div>Foreground layer</div>
  </LayoutSubgrid>
</LayoutSubgrid>

Both subgrids will occupy the same grid row, allowing visual layering.


Paper Texture

A subtle grainy overlay that gives surfaces a tactile, "printed paper" feel.

How It Works

The texture is an SVG-based noise pattern using feTurbulence filter. It's designed to be layered on top of backgrounds using mix-blend-mode: overlay at low opacity.

Exports

Export Type Description
paperTextureCssVars object Individual CSS properties as key-value pairs
paperTextureStyles string Ready-to-use CSS string

paperTextureCssVars

An object containing individual CSS properties:

import { paperTextureCssVars } from "gridelve";

// Properties available:
paperTextureCssVars.img; // The SVG noise pattern as a data URI
paperTextureCssVars.size; // '200px 200px'
paperTextureCssVars.mixBlendMode; // 'overlay'
paperTextureCssVars.opacity; // '0.15'

paperTextureStyles

A pre-composed CSS string for quick application:

import { paperTextureStyles } from "gridelve";

// Returns:
`
  background-image: url("data:image/svg+xml,...");
  background-repeat: repeat;
  background-size: 200px 200px;
  mix-blend-mode: overlay;
  opacity: 0.15;
  pointer-events: none;
`;

Usage with styled-components

Method 1: As a pseudo-element overlay

import styled from "styled-components";
import { paperTextureCssVars } from "gridelve";

const TexturedCard = styled.div`
  position: relative;
  background: var(--ge-bg-secondary);
  padding: 2rem;

  &::before {
    content: "";
    position: absolute;
    inset: 0;
    background-image: ${paperTextureCssVars.img};
    background-repeat: repeat;
    background-size: ${paperTextureCssVars.size};
    mix-blend-mode: ${paperTextureCssVars.mixBlendMode};
    opacity: ${paperTextureCssVars.opacity};
    pointer-events: none;
  }
`;

Method 2: Using the pre-composed string

import styled from "styled-components";
import { paperTextureStyles } from "gridelve";

const TextureOverlay = styled.div`
  position: absolute;
  inset: 0;
  ${paperTextureStyles}
`;

// Usage
<div style={{ position: "relative" }}>
  <TextureOverlay />
  <p>Content with paper texture behind it</p>
</div>;

Usage with Inline Styles

For quick prototyping or non-styled-components contexts:

import { paperTextureCssVars } from "gridelve";

<div style={{ position: "relative", background: "var(--ge-bg-primary)" }}>
  <div
    style={{
      position: "absolute",
      inset: 0,
      backgroundImage: paperTextureCssVars.img,
      backgroundRepeat: "repeat",
      backgroundSize: paperTextureCssVars.size,
      mixBlendMode: paperTextureCssVars.mixBlendMode as any,
      opacity: paperTextureCssVars.opacity,
      pointerEvents: "none",
    }}
  />
  <p>Content here</p>
</div>;

Creating a Reusable PaperOverlay Component

import styled from "styled-components";
import { paperTextureCssVars } from "gridelve";

export const PaperOverlay = styled.div`
  position: absolute;
  inset: 0;
  background-image: ${paperTextureCssVars.img};
  background-repeat: repeat;
  background-size: ${paperTextureCssVars.size};
  mix-blend-mode: ${paperTextureCssVars.mixBlendMode};
  opacity: ${paperTextureCssVars.opacity};
  pointer-events: none;
  z-index: 1;
`;

// Usage in a page section
<LayoutSubgrid style={{ position: "relative" }}>
  <PaperOverlay />
  <BasicPageRow>
    <h1>Content with subtle paper grain</h1>
  </BasicPageRow>
</LayoutSubgrid>;

Customizing the Texture

To adjust intensity or blend mode, override the values:

const SubtleTexture = styled.div`
  position: absolute;
  inset: 0;
  background-image: ${paperTextureCssVars.img};
  background-repeat: repeat;
  background-size: 100px 100px; /* Smaller = finer grain */
  mix-blend-mode: soft-light; /* Softer than overlay */
  opacity: 0.08; /* More subtle */
  pointer-events: none;
`;
Property Effect of Changing
background-size Smaller = finer grain, larger = coarser
opacity Lower = more subtle, higher = more pronounced
mix-blend-mode overlay is standard, soft-light is gentler, multiply for dark themes

Project Structure

/gridelve
├── package.json
├── tsconfig.json
└── src/
    ├── index.ts              # Barrel file (all exports)
    │
    ├── core/                 # Layout primitives
    │   ├── LayoutGrid.tsx    # 12-column math
    │   ├── GridLines.tsx     # Visual line components
    │   ├── BasicPageRow.tsx  # Pre-built section template
    │   ├── CartesianGrid.tsx # Coordinate background
    │   ├── breakpoints.ts    # Viewport constants
    │   ├── fluid-utils.ts    # Responsive scaling
    │   └── animations.ts     # Framer Motion presets
    │
    ├── theme/                # Theming system
    │   ├── ThemeProvider.tsx # React Context + styled-components
    │   └── types.ts          # TypeScript interfaces
    │
    └── styles/               # Global styles
        ├── GlobalStyle.tsx   # CSS reset + variable defaults
        └── textures.ts       # Paper grain background
Directory Responsibility
/core Pure layout logic. No branding.
/theme React Context and theme interface.
/styles CSS reset, default variables, textures.

Quick Reference

// Import everything you need
import {
  // Layout
  LayoutGrid,
  LayoutSubgrid,
  LayoutGridCondensed,
  BasicPageRow,

  // Visual
  GridRule,
  ContentMarginLine,
  CheckeredMargin,
  CartesianGrid,

  // Theme
  GridelveThemeProvider,
  GlobalStyle,

  // Utils
  generateFluidPxScale,
  generateFluidFontSize,
  MEDIA_QUERIES,
} from "./gridelve/src";

About

Professional and aesthetic 12-column grid system for React applications.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors