Updated on 21 Oct 2024
This Pro tutorial provides the steps to arrange the posts output by a Bricks query loop(s) in weekday columns based on the value of a weekday custom field.
We shall cover two different ways of achieving this.
Using data attributes and CSS Grid
This solution is not accessible but has the benefit of being filterable using 3rd party filtering plugins like WP Grid Builder. Uses a single query.

Using custom query types and CSS Grid
This solution is accessible and uses nested (two) query loops. Filtering is possible using custom JavaScript and covered in this tutorial. Third-party filtering plugins will not work with this approach.
Update on 21 Oct 2024: Added steps on how to sort the posts by time in ascending order.

Using data attributes and CSS Grid
Step 1
Create a field group for your post type having a field of type Select or Button Group or any other type of field using your favorite custom fields plugin that enables selection of one option from many.
In this example, let’s go with a Button Group type ACF Field.
Set the choices to
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
Field Name/ID: weekday
Step 2
Edit the Page/template (with Bricks) in which you would like to show the post columns.
Copy and paste the JSON of the fully-built Section from below.

You can observe that each heading has a data-attribute set like this with the value being equal to Monday through Sunday:

Query loop settings:

We are pulling all posts for which weekday field value is not empty.
Data attribute for the query loop element:

HTML output:

Step 3
Add this CSS:
.weekday-grid {
display: grid;
grid-template-columns: repeat(7, minmax(250px, 1fr)); /* Adjust 250px as needed */
grid-auto-flow: column dense;
grid-auto-rows: min-content;
gap: 20px;
overflow-x: auto; /* Enable horizontal scrolling */
padding-bottom: 20px; /* Space for the scrollbar */
}
.weekday-grid h3[data-weekday] {
grid-row: 1;
margin: 0;
padding: 10px;
/* background-color: #f0f0f0; */
/* text-align: center; */
position: sticky;
top: 0;
z-index: 1;
}
.weekday-grid div[data-weekday] {
padding: 10px;
border: 1px solid #e0e0e0;
margin-bottom: 10px;
display: flex;
flex-direction: column;
height: 100%;
background-color: #ffffff;
}
.weekday-grid div[data-weekday] p {
flex-grow: 1;
margin: 0;
}
/* Assigning columns to weekdays */
.weekday-grid *[data-weekday="Monday"] { grid-column: 1; }
.weekday-grid *[data-weekday="Tuesday"] { grid-column: 2; }
.weekday-grid *[data-weekday="Wednesday"] { grid-column: 3; }
.weekday-grid *[data-weekday="Thursday"] { grid-column: 4; }
.weekday-grid *[data-weekday="Friday"] { grid-column: 5; }
.weekday-grid *[data-weekday="Saturday"] { grid-column: 6; }
.weekday-grid *[data-weekday="Sunday"] { grid-column: 7; }
Using custom query types and CSS Grid
Step 1
Create a field group for your post type having a field of type Select or Button Group or any other type of field using your favorite custom fields plugin that enables selection of one option from many.
In this example, let’s go with a Button Group type ACF Field.
Set the choices to
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
Field Name/ID: weekday
Step 2
Add the following in child theme‘s functions.php (w/o the opening PHP tag) or a code snippets plugin:
<?php
// Add new query type controls
add_filter( 'bricks/setup/control_options', 'bl_add_custom_query_types' );
/**
* Add custom query types to Bricks control options.
*
* @param array $control_options The existing control options.
* @return array Modified control options.
*/
function bl_add_custom_query_types( array $control_options ): array {
$control_options['queryTypes']['bl_weekdays'] = esc_html__( 'Weekdays', 'bricks' );
$control_options['queryTypes']['bl_posts_by_weekday'] = esc_html__( 'Weekday Posts', 'bricks' );
return $control_options;
}
// Handle custom query types
add_filter( 'bricks/query/run', 'bl_run_custom_queries', 10, 2 );
/**
* Run custom queries.
*
* @param array $results The existing query results.
* @param BricksQuery $query_obj The query object.
* @return array Modified query results.
*/
function bl_run_custom_queries( array $results, BricksQuery $query_obj ): array {
switch ( $query_obj->object_type ) {
case 'bl_weekdays':
return bl_get_weekdays();
case 'bl_posts_by_weekday':
return bl_get_posts_by_weekday( $query_obj );
default:
return $results;
}
}
/**
* Get array of weekdays.
*
* @return array Array of weekdays.
*/
function bl_get_weekdays(): array {
return [
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
'Sunday',
];
}
/**
* Get posts for a given weekday.
*
* @param BricksQuery $query_obj The query object.
* @return array Array of post objects.
*/
function bl_get_posts_by_weekday( BricksQuery $query_obj ): array {
$weekday = bl_get_loop_object_string();
if ( empty( $weekday ) ) {
return [];
}
$args = [
'post_type' => 'post',
'posts_per_page' => -1, // Get all posts. Limit to a specific number - as small as possible.
'meta_key' => 'weekday',
'meta_value' => $weekday,
];
return get_posts( $args );
}
if ( ! function_exists( 'bl_get_loop_object_string' ) ) {
/**
* Get the current loop object as a string.
*
* @return string The current loop object or an empty string.
*/
function bl_get_loop_object_string(): string {
$looping_query_id = BricksQuery::is_any_looping();
if ( $looping_query_id ) {
$loop_object = BricksQuery::get_loop_object( $looping_query_id );
return (string) $loop_object;
}
return '';
}
}
add_filter( 'bricks/code/echo_function_names', function() {
return [
'bl_get_loop_object_string',
];
} );
In
$args = [
'post_type' => 'post',
'posts_per_page' => -1, // Get all posts. Limit to a specific number - as small as possible.
'meta_key' => 'weekday',
'meta_value' => $weekday,
];
change post (on the right hand side) to your post type identifier.
change weekday meta_key value to the name of your custom field.
We registered two custom query types: ‘Weekdays’ and ‘Weekday Posts’.
Selecting the ‘Weekdays’ query type will enable us to loop through Monday through Sunday.
Selecting the ‘Weekday Posts’ query type will enable us to loop through the posts for the outer ‘Weekdays’ query loop’s current iteration i.e., weekday.
Step 3
Add this CSS:
.weekday-grid {
display: grid;
grid-template-columns: repeat(7, minmax(250px, 1fr));
grid-auto-flow: column dense; /* Fill columns with posts. 'column' directs new items to be placed in the next column, rather than the next row. 'dense' attempts to fill in holes earlier in the grid layout if smaller items come up later.*/
grid-auto-rows: min-content; /* Ensure that the grid rows are only as tall as they need to be to accommodate their content */
overflow-x: auto;
padding-bottom: 20px;
scroll-behavior: smooth;
/*
These properties improve touch scrolling on various devices.
They allow for more natural, "momentum-based" scrolling on touch devices.
*/
-webkit-overflow-scrolling: auto; /* For older iOS support */
overscroll-behavior-x: contain; /* Prevents overscroll on modern browsers */
/*
Improves performance for scrollable areas by creating a new stacking context
and a new containing block for fixed positioned descendants.
*/
will-change: transform;
}
.weekday-column .weekday {
margin: 0;
padding: 1rem;
text-align: left;
}
.weekday-column .weekday-post {
padding: 2rem;
border: 1px solid #e0e0e0;
background-color: #ffffff;
}
.post-list {
list-style-type: none;
padding: 0;
margin: 0;
}
.post-list li {
margin-bottom: var(--content-gap);
}
.post-list a {
display: inline-block;
padding: 0.5rem;
text-decoration: none;
color: inherit;
}
.post-list a:focus {
/* text-decoration: underline; */
outline: 2px solid #000;
outline-offset: 2px;
}
Step 4
Edit the Page/template (with Bricks) in which you would like to show the post columns.
Copy and paste the JSON of the fully-built Section from below.

Select the Code element and sign code.
Outer QL:

Inner QL:

HTML output:

Step 5
Settings (gear icon) → Page Settings → Custom Code → Body (footer) scripts:
<script>
// Wait for the DOM to be fully loaded before running the script
document.addEventListener('DOMContentLoaded', function() {
// Select the main grid container and all weekday columns
const grid = document.querySelector('.weekday-grid');
const columns = grid.querySelectorAll('.weekday-column');
const weekdaySelect = document.getElementById('weekday-select');
// Function to filter weekdays
function filterWeekdays(selectedDay) {
columns.forEach(column => {
if (selectedDay === 'all' || column.dataset.weekday === selectedDay) {
column.style.display = '';
} else {
column.style.display = 'none';
}
});
// Adjust grid layout if filtering
if (selectedDay !== 'all') {
grid.style.gridTemplateColumns = '1fr';
} else {
grid.style.gridTemplateColumns = 'repeat(7, minmax(250px, 1fr))';
}
}
// Add event listener to the dropdown
weekdaySelect.addEventListener('change', function() {
filterWeekdays(this.value);
});
// Iterate through each weekday column
columns.forEach((column, index) => {
// Select the weekday heading and all post links in the current column
const heading = column.querySelector('.weekday');
const posts = column.querySelectorAll('.post-list a');
// Make the heading focusable
heading.setAttribute('tabindex', '0');
// Add keyboard navigation for the weekday heading
heading.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
// When Enter or Space is pressed, focus on the first post in the column
e.preventDefault();
posts[0]?.focus();
} else if (e.key === 'ArrowRight') {
// When Right Arrow is pressed, focus on the next weekday heading
e.preventDefault();
columns[index + 1]?.querySelector('.weekday')?.focus();
} else if (e.key === 'ArrowLeft') {
// When Left Arrow is pressed, focus on the previous weekday heading
e.preventDefault();
columns[index - 1]?.querySelector('.weekday')?.focus();
}
});
// Add keyboard navigation for each post link
posts.forEach((post, postIndex) => {
post.addEventListener('keydown', (e) => {
if (e.key === 'ArrowUp' && postIndex > 0) {
// When Up Arrow is pressed, focus on the previous post (if it exists)
e.preventDefault();
posts[postIndex - 1].focus();
} else if (e.key === 'ArrowDown' && postIndex < posts.length - 1) {
// When Down Arrow is pressed, focus on the next post (if it exists)
e.preventDefault();
posts[postIndex + 1].focus();
} else if (e.key === 'ArrowLeft') {
// When Left Arrow is pressed, focus on the previous weekday heading
e.preventDefault();
columns[index - 1]?.querySelector('.weekday')?.focus();
} else if (e.key === 'ArrowRight') {
// When Right Arrow is pressed, focus on the next weekday heading
e.preventDefault();
columns[index + 1]?.querySelector('.weekday')?.focus();
}
});
});
});
// Make the grid container focusable
grid.setAttribute('tabindex', '0');
// Add keyboard scrolling for the entire grid
grid.addEventListener('keydown', (e) => {
const scrollAmount = 100; // Pixels to scroll
if (e.key === 'ArrowRight') {
// Scroll right when Right Arrow is pressed
e.preventDefault();
grid.scrollLeft += scrollAmount;
} else if (e.key === 'ArrowLeft') {
// Scroll left when Left Arrow is pressed
e.preventDefault();
grid.scrollLeft -= scrollAmount;
}
});
// Initialize filter (show all days by default)
filterWeekdays('all');
});
</script>
Update 1: How to sort the posts by time in ascending order

ACF
Add a Time Picker type of field named say, time.
Return format: H:i:s
Edit the posts and set time for each.
In Step 2, replace
/**
* Get posts for a given weekday.
*
* @param BricksQuery $query_obj The query object.
* @return array Array of post objects.
*/
function bl_get_posts_by_weekday( BricksQuery $query_obj ): array {
$weekday = bl_get_loop_object_string();
if ( empty( $weekday ) ) {
return [];
}
$args = [
'post_type' => 'post',
'posts_per_page' => -1, // Get all posts. Limit to a specific number - as small as possible.
'meta_key' => 'weekday',
'meta_value' => $weekday,
];
return get_posts( $args );
}
with
/**
* Get posts for a given weekday, ordered by time.
*
* @param BricksQuery $query_obj The query object.
* @return array Array of post objects.
*/
function bl_get_posts_by_weekday( BricksQuery $query_obj ): array {
$weekday = bl_get_loop_object_string();
if ( empty( $weekday ) ) {
return [];
}
$args = [
'post_type' => 'post',
'posts_per_page' => -1, // Consider limiting this for performance
'meta_query' => [
'relation' => 'AND',
[
'key' => 'weekday',
'value' => $weekday,
],
[
'key' => 'time', // ACF Time Picker field name
'compare' => 'EXISTS',
],
],
'orderby' => 'meta_value',
'meta_key' => 'time',
'order' => 'ASC',
];
$posts = get_posts( $args );
// Sort posts by time
usort( $posts, function( $a, $b ) {
$time_a = get_post_meta( $a->ID, 'time', true );
$time_b = get_post_meta( $b->ID, 'time', true );
// Times are already in 'H:i:s' format, so we can compare them directly
return strcmp( $time_a, $time_b );
});
return $posts;
}
To display the time in a human-friendly format on the front end, use this dynamic data tag:
{acf_time:g:i a}
Ex.: 7:30 pm