DEV3 Technologies home

Weekly class timetable grid

Turn a flat list of sessions into an accessible day-by-time table with a shortcode, escaped output and empty cell handling.

WordPressLayoutstimetableschedulegridshortcode

Live preview

Code

<?php
/**
 * [acme_timetable] weekly grid.
 *
 * @package Acme_Theme
 */

defined( 'ABSPATH' ) || exit;

/** Sessions: day (1 = Monday), start time, title, coach. Replace with real data. */
function acme_timetable_sessions(): array {
	return apply_filters( 'acme_timetable_sessions', array(
		array( 'day' => 1, 'time' => '07:00', 'title' => 'Morning flow', 'coach' => 'Ana' ),
		array( 'day' => 1, 'time' => '18:00', 'title' => 'Strength', 'coach' => 'Ben' ),
		array( 'day' => 3, 'time' => '18:00', 'title' => 'Mobility', 'coach' => 'Ana' ),
		array( 'day' => 5, 'time' => '07:00', 'title' => 'Intervals', 'coach' => 'Cleo' ),
	) );
}

function acme_timetable_shortcode(): string {
	wp_enqueue_style( 'acme-timetable', get_template_directory_uri() . '/assets/timetable.css', array(), '1.0.0' );

	$grid = array();
	foreach ( acme_timetable_sessions() as $s ) {
		$grid[ $s['time'] ][ (int) $s['day'] ][] = $s;
	}
	ksort( $grid );

	$days = array( 1 => 'Mon', 2 => 'Tue', 3 => 'Wed', 4 => 'Thu', 5 => 'Fri', 6 => 'Sat', 7 => 'Sun' );
	$html = '<div class="acme-tt-wrap" tabindex="0"><table class="acme-tt"><thead><tr><th scope="col"><span class="screen-reader-text">' . esc_html__( 'Time', 'acme-theme' ) . '</span></th>';
	foreach ( $days as $label ) {
		$html .= '<th scope="col">' . esc_html( $label ) . '</th>';
	}
	$html .= '</tr></thead><tbody>';

	foreach ( $grid as $time => $row ) {
		$html .= '<tr><th scope="row">' . esc_html( $time ) . '</th>';
		foreach ( array_keys( $days ) as $n ) {
			$html .= '<td>';
			foreach ( $row[ $n ] ?? array() as $s ) {
				$html .= sprintf( '<div class="acme-tt__class"><strong>%s</strong><span>%s</span></div>', esc_html( $s['title'] ), esc_html( $s['coach'] ) );
			}
			$html .= '</td>';
		}
		$html .= '</tr>';
	}
	return $html . '</tbody></table></div>';
}
add_shortcode( 'acme_timetable', 'acme_timetable_shortcode' );

How it works

What it solves

Gyms and studios publish the same weekly schedule over and over. A grid with days across and start times down is the format people expect, and it should be readable by screen readers and scroll sideways on small screens.

How it works

Sessions come from a filterable PHP array so another plugin or option can replace them. The code groups sessions by time and weekday into a two-level array, sorts the times, then prints a table with proper th scope attributes for both axes. Cells without a class stay empty rather than being omitted, so the columns line up.

The wrapper has overflow-x: auto and tabindex="0" so keyboard users can scroll it.

Adapting it

Feed the filter from a custom post type or repeater field. Add a data attribute for the class type and colour cells with CSS, or add a small script to filter by coach. Switch the day labels to wp_date( 'D' ) for localised names. If sessions can overlap, loop over them as the code already does.

Pitfalls

  • Sort times as H:i strings, which sort correctly when zero padded.
  • Do not use a table purely for layout elsewhere, but this is genuine tabular data.
  • Provide a visible caption or heading near the table for context.
  • Sunday-first locales may need a different order.

How to use it

Add the PHP and CSS files, then require inc/timetable.php from functions.php.

Place [acme_timetable] on a page.

Hook acme_timetable_sessions to return your own list of sessions.

Licence: GPL-2.0-or-later. This snippet comes from WordPress code and is GPL-2.0-or-later.

Related snippets