Add RAR Storefront WordPress theme

Implements the Reply All Regrets™ frontend design: retro office aesthetic
with manila/ink/stamp-red/olive palette, Fraunces + JetBrains Mono + Inter
typography. Includes homepage, category, product detail, blog, about, cart
templates with WooCommerce integration and interactive JS (filter, sort, gallery).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
FrontendDev
2026-05-02 20:38:29 -04:00
parent ca4870649a
commit 1cc5a8ecfe
14 changed files with 2736 additions and 0 deletions

View File

@@ -0,0 +1,236 @@
<?php
/**
* WooCommerce Archive/Category page — shop product listing with filter sidebar + sort
*/
defined('ABSPATH') || exit;
get_header('shop');
$term = get_queried_object();
$is_category = $term instanceof WP_Term;
$cat_name = $is_category ? $term->name : 'Shop';
$cat_desc = $is_category ? $term->description : '';
$cat_count = $is_category ? $term->count : wp_count_posts('product')->publish;
$cat_slug = $is_category ? $term->slug : 'shop';
// Category descriptions
$cat_desc_map = [
'mugs' => '47 ceramics, 12 enamels, and a few we honestly forgot to count. Dishwasher safe. Microwave safe. HR safe (mostly).',
'canvas' => 'Print-on-demand wall art for the soul-tired and decoratively passive-aggressive.',
'planners' => 'For the person who plans a lot and does a little. Or the other way around.',
'apparel' => 'Wear your grievances. Business casual with a quiet fury.',
];
if (empty($cat_desc)) {
$cat_desc = $cat_desc_map[$cat_slug] ?? 'The complete collection. Curated for the chronically employed.';
}
// Sort order
$orderby = isset($_GET['orderby']) ? sanitize_text_field($_GET['orderby']) : 'menu_order';
?>
<!-- Breadcrumb -->
<div class="rar-breadcrumb">
<a href="<?php echo esc_url(home_url('/')); ?>">Shop</a> /
<?php if ($is_category) : ?>
<a href="<?php echo esc_url(home_url('/shop/')); ?>">Departments</a> /
<span><?php echo esc_html($cat_name); ?></span>
<?php else : ?>
<span>All Products</span>
<?php endif; ?>
</div>
<!-- Category Hero -->
<div class="rar-cat-hero">
<?php if ($is_category) : ?>
<div class="rar-folder-tab">DEPT. <?php echo strtoupper($cat_slug); ?> · <?php echo esc_html(strtoupper($cat_name)); ?></div>
<?php endif; ?>
<div class="rar-cat-hero__inner">
<div>
<h1>
<?php echo esc_html($cat_name); ?>.<br>
<em style="color:var(--stamp);font-style:italic;"><?php echo esc_html(wp_trim_words($cat_desc, 6)); ?></em>
</h1>
</div>
<div>
<p style="font-size:14px;line-height:1.6;color:var(--ink-muted);margin:0 0 18px;max-width:380px;"><?php echo esc_html($cat_desc); ?></p>
<div class="rar-cat-hero__stats">
<span><?php echo esc_html($cat_count); ?> ITEMS</span>
<span>·</span>
<span>FREE SHIP OVER $40</span>
</div>
</div>
</div>
</div>
<!-- Toolbar -->
<div class="rar-toolbar">
<div class="rar-toolbar__left">
Showing
<?php woocommerce_result_count(); ?>
</div>
<div class="rar-toolbar__right">
<!-- Grid/List toggle -->
<div class="rar-view-toggle">
<button class="rar-view-toggle__btn active" id="rar-grid-btn" onclick="rarSetView('grid')">Grid</button>
<button class="rar-view-toggle__btn" id="rar-list-btn" onclick="rarSetView('list')">List</button>
</div>
<!-- Sort -->
<div class="rar-sort-wrap">
<button class="rar-sort-btn" onclick="document.getElementById('rar-sort-menu').classList.toggle('open')">
Sort: <span id="rar-sort-label"><?php echo esc_html(wc_get_loop_prop('orderby', 'Featured')); ?></span>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"></path></svg>
</button>
<div class="rar-sort-menu" id="rar-sort-menu">
<?php
$sort_options = WC()->query->get_catalog_ordering_args();
foreach (wc_get_catalog_ordering_args() as $val => $opt) {
$current = (isset($_GET['orderby']) ? $_GET['orderby'] : get_option('woocommerce_default_catalog_orderby')) === $val;
$url = add_query_arg('orderby', $val);
echo '<a href="' . esc_url($url) . '" class="' . ($current ? 'active' : '') . '" onclick="document.getElementById(\'rar-sort-label\').textContent=this.textContent;document.getElementById(\'rar-sort-menu\').classList.remove(\'open\');">' . esc_html($opt) . '</a>';
}
?>
</div>
</div>
</div>
</div>
<!-- Category layout: filter sidebar + product grid -->
<div class="rar-cat-layout">
<!-- Filter Sidebar -->
<aside class="rar-filter-sidebar">
<div class="rar-form-tag" style="margin-bottom:14px;">FORM 06-B · FILTERS</div>
<!-- Subcategories -->
<?php if ($is_category) {
$subcats = get_terms(['taxonomy' => 'product_cat', 'parent' => $term->term_id, 'hide_empty' => true]);
if (!empty($subcats)) {
echo '<div class="rar-filter-group">';
echo '<div class="rar-filter-group__label">Subcategories <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14"></path></svg></div>';
foreach ($subcats as $sc) {
$active = (isset($_GET['product_cat']) && $_GET['product_cat'] === $sc->slug) ? ' checked' : '';
echo '<label class="rar-filter-item"><input type="checkbox" name="product_cat" value="' . esc_attr($sc->slug) . '"' . $active . ' onchange="rarApplyFilter(this)"> ' . esc_html($sc->name) . ' <span style="font-family:var(--mono);font-size:10px;color:var(--ink-faint);margin-left:auto;">' . esc_html($sc->count) . '</span></label>';
}
echo '</div>';
}
} ?>
<!-- Price filter -->
<div class="rar-filter-group">
<div class="rar-filter-group__label">
Price
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14"></path></svg>
</div>
<?php
$price_ranges = [
['under-25', 'Under $25', '0', '25'],
['25-40', '$25 $40', '25', '40'],
['over-40', 'Over $40', '40', '999'],
];
foreach ($price_ranges as [$id, $label, $min, $max]) {
$active = (isset($_GET['min_price']) && $_GET['min_price'] === $min) ? ' checked' : '';
echo '<label class="rar-filter-item"><input type="checkbox" id="price-' . esc_attr($id) . '" data-min="' . esc_attr($min) . '" data-max="' . esc_attr($max) . '"' . $active . ' onchange="rarPriceFilter(this)"> ' . esc_html($label) . '</label>';
}
?>
</div>
<!-- In-stock filter -->
<div class="rar-filter-group">
<div class="rar-filter-group__label">
Availability
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14"></path></svg>
</div>
<label class="rar-filter-item">
<input type="checkbox" id="rar-in-stock" <?php echo (isset($_GET['availability']) && $_GET['availability'] === 'instock') ? 'checked' : ''; ?> onchange="rarStockFilter(this)">
In Stock Only
</label>
</div>
<?php if (is_active_sidebar('shop-filter')) : ?>
<?php dynamic_sidebar('shop-filter'); ?>
<?php endif; ?>
<div class="rar-filter-apply">
<button class="rar-btn" style="width:100%;" onclick="rarApplyAllFilters()">Apply filters</button>
</div>
</aside>
<!-- Product Grid -->
<div>
<div class="rar-product-grid" id="rar-product-grid">
<?php
if (have_posts()) :
while (have_posts()) :
the_post();
global $product;
if (!$product) $product = wc_get_product(get_the_ID());
if (!$product) continue;
$img_id = $product->get_image_id();
$img_url = $img_id ? wp_get_attachment_image_url($img_id, 'rar-product-card') : '';
$sku = $product->get_sku() ?: 'SKU-' . $product->get_id();
$cats = get_the_terms(get_the_ID(), 'product_cat');
$catname = $cats ? $cats[0]->name : '';
$price = $product->get_price_html();
$url = get_permalink();
$stock = $product->get_stock_status();
$tag = $product->is_featured() ? 'Featured' : '';
if ($product->is_on_sale()) $tag = 'Sale';
if ($stock === 'onbackorder') $tag = 'Backorder';
$stock_label = $stock === 'instock' ? 'IN STOCK' : ($stock === 'onbackorder' ? 'BACKORDER' : 'OUT OF STOCK');
$stock_color = $stock === 'instock' ? 'var(--olive-dark)' : ($stock === 'onbackorder' ? 'var(--ink-faint)' : 'var(--stamp)');
?>
<a href="<?php echo esc_url($url); ?>" class="rar-card">
<div class="rar-card__media">
<div class="rar-card__sku">SKU <?php echo esc_html($sku); ?></div>
<?php if ($tag) : ?>
<div class="rar-card__badge"><?php echo esc_html($tag); ?></div>
<?php endif; ?>
<?php if ($img_url) : ?>
<img src="<?php echo esc_url($img_url); ?>" alt="<?php echo esc_attr($product->get_name()); ?>" />
<?php else : ?>
<div class="rar-placeholder__label"><?php echo esc_html(strtoupper($catname ?: $product->get_name())); ?></div>
<?php endif; ?>
</div>
<div class="rar-card__body">
<?php if ($catname) : ?>
<div class="rar-card__cat"><?php echo esc_html($catname); ?></div>
<?php endif; ?>
<div class="rar-card__title"><?php echo esc_html($product->get_name()); ?></div>
<div class="rar-card__row">
<span class="rar-card__price"><?php echo wp_kses_post($price); ?></span>
<span style="color:<?php echo esc_attr($stock_color); ?>;font-family:var(--mono);font-size:9px;letter-spacing:0.18em;"><?php echo esc_html($stock_label); ?></span>
</div>
</div>
</a>
<?php
endwhile;
else :
echo '<p style="font-family:var(--serif);font-size:18px;font-style:italic;color:var(--ink-muted);grid-column:1/-1;">No products found. The warehouse is confused.</p>';
endif;
?>
</div>
<!-- Pagination -->
<div class="rar-pagination">
<span class="rar-mono" style="font-size:10px;letter-spacing:0.16em;text-transform:uppercase;color:var(--ink-muted);">
Page <?php echo max(1, get_query_var('paged')); ?> of <?php echo max(1, $wp_query->max_num_pages); ?>
</span>
<div class="rar-page-btns">
<?php
the_posts_pagination([
'mid_size' => 2,
'prev_text' => '',
'next_text' => '',
'screen_reader_text' => ' ',
'before_page_number' => '',
'after_page_number' => '',
]);
?>
</div>
</div>
</div>
</div>
<?php get_footer('shop'); ?>

View File

@@ -0,0 +1,209 @@
<?php
/**
* WooCommerce Single Product — gallery + variants + add-to-cart + reviews
*/
defined('ABSPATH') || exit;
get_header('shop');
the_post();
global $product;
if (!$product) $product = wc_get_product(get_the_ID());
$sku = $product->get_sku() ?: 'SKU-' . $product->get_id();
$cats = get_the_terms(get_the_ID(), 'product_cat');
$catname = $cats ? $cats[0]->name : 'Product';
$cat_url = $cats ? get_term_link($cats[0]) : get_permalink(wc_get_page_id('shop'));
$gallery_ids = $product->get_gallery_image_ids();
$main_img_id = $product->get_image_id();
$all_imgs = $main_img_id ? array_merge([$main_img_id], $gallery_ids) : $gallery_ids;
$price = $product->get_price_html();
$stock = $product->get_stock_status();
$stock_label = $stock === 'instock' ? '● IN STOCK' : ($stock === 'onbackorder' ? '◐ BACKORDER' : '○ OUT OF STOCK');
$stock_color = $stock === 'instock' ? 'var(--olive-dark)' : ($stock === 'onbackorder' ? 'var(--stamp)' : 'var(--ink-faint)');
?>
<!-- Breadcrumb -->
<div class="rar-breadcrumb">
<a href="<?php echo esc_url(home_url('/')); ?>">Shop</a> /
<a href="<?php echo esc_url($cat_url); ?>"><?php echo esc_html($catname); ?></a> /
<span><?php the_title(); ?></span>
</div>
<!-- Product layout -->
<div class="rar-product-detail">
<!-- Gallery -->
<div class="rar-gallery" id="rar-gallery">
<div class="rar-gallery__thumbs">
<?php foreach ($all_imgs as $i => $img_id) :
$thumb_url = wp_get_attachment_image_url($img_id, 'thumbnail');
$full_url = wp_get_attachment_image_url($img_id, 'rar-product-card');
?>
<button class="rar-gallery__thumb <?php echo $i === 0 ? 'active' : ''; ?>"
onclick="rarSwitchGallery(<?php echo esc_attr($i); ?>, '<?php echo esc_url($full_url); ?>')"
aria-label="View image <?php echo esc_attr($i + 1); ?>">
<?php if ($thumb_url) : ?>
<img src="<?php echo esc_url($thumb_url); ?>" alt="Image <?php echo esc_attr($i + 1); ?>" />
<?php endif; ?>
<span style="position:absolute;bottom:4px;left:4px;font-family:var(--mono);font-size:8px;color:var(--ink-soft);background:var(--paper);padding:1px 4px;border:1px solid var(--ink);">0<?php echo esc_html($i + 1); ?></span>
</button>
<?php endforeach; ?>
<?php if (empty($all_imgs)) : ?>
<button class="rar-gallery__thumb active" aria-label="Default image">
<span style="position:absolute;bottom:4px;left:4px;font-family:var(--mono);font-size:8px;color:var(--ink-soft);background:var(--paper);padding:1px 4px;border:1px solid var(--ink);">01</span>
</button>
<?php endif; ?>
</div>
<div class="rar-gallery__main" id="rar-gallery-main">
<?php if (!empty($all_imgs)) :
$main_url = wp_get_attachment_image_url($all_imgs[0], 'rar-product-card');
?>
<img id="rar-gallery-img" src="<?php echo esc_url($main_url); ?>" alt="<?php the_title_attribute(); ?>" />
<?php else : ?>
<div class="rar-placeholder__label"><?php echo esc_html(strtoupper($catname)); ?></div>
<?php endif; ?>
<div class="rar-gallery__label" id="rar-gallery-label">FIG. 1 — FRONT</div>
<div style="position:absolute;bottom:18px;left:18px;">
<span class="rar-stamp">SKU <?php echo esc_html($sku); ?></span>
</div>
<div style="position:absolute;bottom:18px;right:18px;font-family:var(--mono);font-size:9px;letter-spacing:0.18em;text-transform:uppercase;color:var(--ink-muted);background:var(--paper);padding:4px 8px;border:1px solid var(--hairline-strong);">
<span id="rar-gallery-counter">1</span> / <?php echo esc_html(max(1, count($all_imgs))); ?>
</div>
</div>
</div>
<!-- Product Info -->
<div class="rar-product-info">
<div class="rar-form-tag" style="margin-bottom:14px;">
<?php echo esc_html(strtoupper($catname)); ?> · <?php echo esc_html(strtoupper($product->get_type())); ?> · <?php echo esc_html($sku); ?>
</div>
<h1><?php the_title(); ?></h1>
<?php if ($product->get_short_description()) : ?>
<p class="rar-product-subtitle"><?php echo wp_kses_post($product->get_short_description()); ?></p>
<?php endif; ?>
<!-- Rating -->
<div style="display:flex;align-items:center;gap:16px;margin-bottom:24px;">
<?php if ($product->get_rating_count() > 0) : ?>
<?php echo wc_get_rating_html($product->get_average_rating()); ?>
<span class="rar-mono" style="font-size:11px;letter-spacing:0.06em;">
<?php echo esc_html($product->get_average_rating()); ?> · <?php echo esc_html($product->get_rating_count()); ?> reviews
</span>
<span style="color:var(--hairline-strong);">·</span>
<?php endif; ?>
<span class="rar-mono" style="font-size:10px;letter-spacing:0.18em;text-transform:uppercase;color:<?php echo esc_attr($stock_color); ?>;">
<?php echo esc_html($stock_label); ?>
</span>
</div>
<!-- Price -->
<div class="rar-product-price"><?php echo wp_kses_post($price); ?></div>
<div class="rar-product-price-sub">USD · ship $4.50 · free over $40</div>
<!-- Description -->
<?php if ($product->get_description()) : ?>
<div class="rar-product-desc"><?php echo wp_kses_post(wp_trim_words($product->get_description(), 40)); ?></div>
<?php endif; ?>
<!-- Add to Cart Form -->
<?php woocommerce_template_single_add_to_cart(); ?>
<!-- Specs from short description / attributes -->
<?php $attrs = $product->get_attributes(); ?>
<?php if (!empty($attrs)) : ?>
<div class="rar-spec-list">
<div class="rar-spec-list__label">SECTION 4-A · SPECIFICATIONS</div>
<ul>
<?php $i = 1; foreach ($attrs as $attr) :
$attr_name = wc_attribute_label($attr->get_name());
$attr_vals = $attr->is_taxonomy()
? wc_get_product_terms($product->get_id(), $attr->get_name(), ['fields' => 'names'])
: $attr->get_options();
?>
<li>
<span class="rar-spec-num"><?php echo str_pad($i++, 2, '0', STR_PAD_LEFT); ?>.</span>
<span><strong><?php echo esc_html($attr_name); ?>:</strong> <?php echo esc_html(implode(', ', $attr_vals)); ?></span>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<!-- Shipping meta -->
<div class="rar-shipping-grid">
<div class="rar-shipping-item">
<div class="rar-shipping-item__key">Ships</div>
<div class="rar-shipping-item__val">In 3-5 biz days · USPS</div>
</div>
<div class="rar-shipping-item">
<div class="rar-shipping-item__key">Returns</div>
<div class="rar-shipping-item__val">30-day grievance window</div>
</div>
<div class="rar-shipping-item">
<div class="rar-shipping-item__key">Origin</div>
<div class="rar-shipping-item__val">Cleveland, OH</div>
</div>
<div class="rar-shipping-item">
<div class="rar-shipping-item__key">Stock</div>
<div class="rar-shipping-item__val"><?php echo esc_html($product->get_stock_quantity() ?? '—'); ?> units</div>
</div>
</div>
</div>
</div>
<!-- Product tabs: description, reviews -->
<div style="padding:0 56px 64px;">
<?php woocommerce_output_product_data_tabs(); ?>
</div>
<!-- Reviews -->
<?php if ($product->get_rating_count() > 0) : ?>
<section class="rar-reviews">
<?php rar_section_head('FORM 12-C · TESTIMONIALS', 'What the chronically CC\'d are saying.', esc_html($product->get_rating_count()) . ' REVIEWS<br/>AVG. ' . esc_html($product->get_average_rating()) . ' ★'); ?>
<?php comments_template(); ?>
</section>
<?php endif; ?>
<!-- Related Products -->
<?php
$related_ids = wc_get_related_products($product->get_id(), 3);
if (!empty($related_ids)) :
$related_products = array_filter(array_map('wc_get_product', $related_ids));
?>
<section class="rar-products" style="background:var(--paper);border-top:1px solid var(--hairline-strong);">
<?php rar_section_head('FORM 13 · RELATED ITEMS', 'You might also regret buying...'); ?>
<div class="rar-products__grid">
<?php foreach ($related_products as $rp) :
$ri = $rp->get_image_id();
$ru = $ri ? wp_get_attachment_image_url($ri, 'rar-product-card') : '';
$rc = get_the_terms($rp->get_id(), 'product_cat');
$rcat = $rc ? $rc[0]->name : '';
?>
<a href="<?php echo esc_url(get_permalink($rp->get_id())); ?>" class="rar-card">
<div class="rar-card__media">
<div class="rar-card__sku">SKU <?php echo esc_html($rp->get_sku() ?: $rp->get_id()); ?></div>
<?php if ($ru) : ?>
<img src="<?php echo esc_url($ru); ?>" alt="<?php echo esc_attr($rp->get_name()); ?>" />
<?php else : ?>
<div class="rar-placeholder__label"><?php echo esc_html(strtoupper($rcat ?: $rp->get_name())); ?></div>
<?php endif; ?>
</div>
<div class="rar-card__body">
<?php if ($rcat) : ?><div class="rar-card__cat"><?php echo esc_html($rcat); ?></div><?php endif; ?>
<div class="rar-card__title"><?php echo esc_html($rp->get_name()); ?></div>
<div class="rar-card__row">
<span class="rar-card__price"><?php echo wp_kses_post($rp->get_price_html()); ?></span>
<span style="color:var(--ink-muted);">→</span>
</div>
</div>
</a>
<?php endforeach; ?>
</div>
</section>
<?php endif; ?>
<?php get_footer('shop'); ?>