shaduler
Recipes

Many resources / horizontal scroll

100 resources with minColumnWidth — the grid scrolls horizontally instead of squishing.

When you have more columns than horizontal space, set minColumnWidth and the calculator will sum widths and let CSS handle horizontal overflow. The time column stays sticky.

Tech 1
Tech 2
Tech 3
Tech 4
Tech 5
Tech 6
Tech 7
Tech 8
Tech 9
Tech 10
Tech 11
Tech 12
Tech 13
Tech 14
Tech 15
Tech 16
Tech 17
Tech 18
Tech 19
Tech 20
Tech 21
Tech 22
Tech 23
Tech 24
Tech 25
Tech 26
Tech 27
Tech 28
Tech 29
Tech 30
Tech 31
Tech 32
Tech 33
Tech 34
Tech 35
Tech 36
Tech 37
Tech 38
Tech 39
Tech 40
Tech 41
Tech 42
Tech 43
Tech 44
Tech 45
Tech 46
Tech 47
Tech 48
Tech 49
Tech 50
Tech 51
Tech 52
Tech 53
Tech 54
Tech 55
Tech 56
Tech 57
Tech 58
Tech 59
Tech 60
Tech 61
Tech 62
Tech 63
Tech 64
Tech 65
Tech 66
Tech 67
Tech 68
Tech 69
Tech 70
Tech 71
Tech 72
Tech 73
Tech 74
Tech 75
Tech 76
Tech 77
Tech 78
Tech 79
Tech 80
Tech 81
Tech 82
Tech 83
Tech 84
Tech 85
Tech 86
Tech 87
Tech 88
Tech 89
Tech 90
Tech 91
Tech 92
Tech 93
Tech 94
Tech 95
Tech 96
Tech 97
Tech 98
Tech 99
Tech 100
08:00
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
Battery check
Diagnostics
Tire rotation
Wheel balance
Battery check
Tire rotation
Brake repair
Inspection
Service call
Alignment
Service call
Diagnostics
Oil change
Diagnostics
Service call
Battery check
AC service
Inspection
Tire rotation
Service call
Wheel balance
Alignment
Brake repair
Brake repair
Wheel balance
Alignment
Alignment
Inspection
Wheel balance
Tire rotation
Inspection
Diagnostics
Battery check
Battery check
Wheel balance
Oil change
Battery check
Diagnostics
Service call
Diagnostics
Service call
Brake repair
Brake repair
Tire rotation
Oil change
Oil change
Brake repair
Diagnostics
Diagnostics
Oil change
Service call
Oil change
Alignment
Alignment
Alignment
Alignment
Brake repair
Alignment
Service call
Battery check
Alignment
Alignment
Tire rotation
AC service
Alignment
Service call
Wheel balance
Tire rotation
Inspection
Tire rotation
Wheel balance
Battery check
Tire rotation
Oil change
Diagnostics
Battery check
Inspection
Alignment
Service call
Diagnostics
Service call
Battery check
Alignment
Service call
Battery check
AC service
Diagnostics
Diagnostics
Brake repair
Battery check
Oil change
Brake repair
Wheel balance
Battery check
Alignment
Tire rotation
Battery check
Inspection
Tire rotation
Wheel balance
Alignment
Tire rotation
Battery check
Battery check
Inspection
Oil change
Battery check
Diagnostics
Battery check
Oil change
Diagnostics
Diagnostics
Brake repair
Battery check
Service call
Diagnostics
Alignment
Service call
Diagnostics
Inspection
Tire rotation
Tire rotation
Brake repair
Wheel balance
Battery check
Diagnostics
Wheel balance
Diagnostics
Wheel balance
Tire rotation
Brake repair
Service call
Diagnostics
Battery check
AC service
Diagnostics
Tire rotation
Service call
Tire rotation
Service call
Wheel balance
Service call
Tire rotation
Tire rotation
Service call
Oil change
Diagnostics
Tire rotation
Tire rotation
Inspection
Alignment
AC service
Tire rotation
Battery check
AC service
AC service
Service call
Tire rotation
Tire rotation
Diagnostics
Wheel balance
Diagnostics
Battery check
Wheel balance
Alignment
Alignment
Alignment
Tire rotation
Brake repair
Wheel balance
Alignment
Inspection
Wheel balance
Battery check
Service call
AC service
Brake repair
Service call
Oil change
Service call
Alignment
Tire rotation
Inspection
Diagnostics
Tire rotation
AC service
Oil change
AC service
Oil change
Service call
AC service
Diagnostics
Brake repair
Diagnostics
Brake repair
Inspection
Battery check
Brake repair
Battery check
Battery check
Service call
Oil change
Diagnostics
Battery check
Diagnostics
Oil change
Service call
Alignment
Tire rotation
Battery check
Oil change
Brake repair
Alignment
Brake repair
Brake repair
Alignment
AC service
Brake repair
Oil change
Alignment
Tire rotation
Brake repair
Tire rotation
Service call
Wheel balance
AC service
Oil change
Brake repair
Alignment
Service call
Service call
Inspection
Tire rotation
Oil change
Tire rotation
AC service
Inspection
AC service
Diagnostics
Diagnostics
AC service
Inspection
Battery check
Battery check
Alignment
Oil change
AC service
Service call
AC service
AC service
Oil change
Battery check
Brake repair
Battery check
Alignment
Inspection
Battery check
Tire rotation
Tire rotation
Battery check
Alignment
Inspection
Tire rotation
AC service
AC service
Alignment
Oil change
AC service
Wheel balance
Oil change
Service call
Diagnostics
Wheel balance
Oil change
Wheel balance
Battery check
Brake repair
Tire rotation
Oil change
Wheel balance
Alignment
Oil change
Service call
Inspection
Diagnostics
AC service
Alignment
Oil change
Wheel balance
Diagnostics
Diagnostics
Battery check
AC service
Wheel balance
Inspection
Brake repair
Oil change
Diagnostics
Brake repair
Alignment
AC service
Oil change
Alignment
Wheel balance
Tire rotation
Tire rotation
Battery check
Alignment
Inspection
Brake repair
Service call
Wheel balance
Brake repair
AC service
Alignment
Diagnostics
Tire rotation
Battery check
AC service
AC service
Inspection
Battery check
AC service
Diagnostics
Brake repair
Wheel balance
AC service
Battery check
Brake repair
Wheel balance
Diagnostics
Inspection
AC service
Alignment
Oil change
Alignment
Battery check
Diagnostics
AC service
Alignment
Wheel balance
Brake repair
Alignment
Oil change
Alignment
Inspection
Alignment
Brake repair
Battery check
AC service
Brake repair
Inspection
Oil change
Tire rotation
Tire rotation
Wheel balance
Diagnostics
Alignment
Battery check
Battery check
Battery check
const START_HOUR = 8
const END_HOUR = 19
const HOUR_HEIGHT_PX = 60
const TIME_COLUMN_WIDTH_PX = 72   // left gutter
const MIN_COLUMN_WIDTH_PX = 140   // each lane is ≥ 140px; below that, scroll
const COLUMN_COUNT = 100
const SNAP_MIN = 15

const cols = Array.from({ length: COLUMN_COUNT }, (_, i) => ({
  id: `r${i + 1}`,
  label: `Tech ${i + 1}`,
}))

const calc = calculateShadulerData(
  cols,
  tasks,
  START_HOUR,
  END_HOUR,
  HOUR_HEIGHT_PX,
  {
    timeColumnWidth: TIME_COLUMN_WIDTH_PX,
    minColumnWidth: MIN_COLUMN_WIDTH_PX,
  },
)

Total grid width here is 100 × 140 + 72 = 14072px — the wrapper scrolls horizontally and the time column stays sticky on the left.

Generating sample data

For a demo at this scale, the tasks are generated with a seeded PRNG so that server-side render and client hydration produce the same list (Math.random() would mismatch and trigger a hydration warning).

function makeRng(seed: number) {
  let s = seed >>> 0
  return () => {
    s = (s + 0x6d2b79f5) >>> 0
    let t = s
    t = Math.imul(t ^ (t >>> 15), t | 1)
    t ^= t + Math.imul(t ^ (t >>> 7), t | 61)
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296
  }
}

const rng = makeRng(42)
const END_MIN = END_HOUR * 60

// Sequential cursor: each task starts after the previous + a small gap,
// so tasks inside a column never overlap. Stops early if we'd exceed END_HOUR.
const tasks = cols.flatMap((col) => {
  const count = 2 + Math.floor(rng() * 4) // up to 2–5 tasks per column
  const out: ShadulerTaskData[] = []
  let cursorMin = START_HOUR * 60
  for (let j = 0; j < count; j++) {
    const gapMin = Math.floor(rng() * 7) * SNAP_MIN          // 0–90 min gap
    const durationMin = (2 + Math.floor(rng() * 5)) * SNAP_MIN // 30–90 min
    cursorMin += gapMin
    if (cursorMin + durationMin > END_MIN) break
    out.push({
      id: `${col.id}-t${j}`,
      column: col.id,
      name: TASK_NAMES[Math.floor(rng() * TASK_NAMES.length)],
      startTime: minutesToTime(cursorMin),
      endTime: minutesToTime(cursorMin + durationMin),
    })
    cursorMin += durationMin
  }
  return out
})

Tasks scroll with the grid; column headers and the time column track via CSS sticky.

On this page