shaysart.net

Shay’s Art Site — Build Plan

Overview

A lightweight, custom WordPress theme for an artist’s portfolio. The goal is a clean, human, gallery-first experience — a place to show the work. Selling is a bonus, not a funnel. No page builders, no bloat, no automated store. Just paintings, a way to say hello, and a bit about the artist.


Guiding Principles

  • It’s a gallery, not a shop — contact is an invitation, not a checkout
  • Minimal footprint — custom theme + custom plugin; as few third-party plugins as possible; fits on existing shared hosting
  • Black backgrounds everywhere — paintings float in darkness, no visual noise
  • Warm and human — language throughout is relaxed, not transactional
  • Social-first traffic — proper Open Graph / Twitter Card meta tags so shared links preview beautifully
  • Mobile-first responsive — designed for a phone screen first, scales up gracefully

Site Structure

PageWordPress TypeNotes
Gallery (Home)Custom templateResponsive thumbnail grid
Single PaintingCustom post type: paintingZoomable image + title + soft contact link
AboutStandard PageWho Shay is — carries real weight on this kind of site
ContactStandard PageSimple, warm form via custom plugin

Navigation — No Menu Bar

With only three destination pages (Gallery, About, Contact) there’s no need for a traditional nav bar eating into the painting space. Instead:

  • Site title (“Shay’s Art”) at the top of every page links back to the gallery
  • Contextual links appear where they naturally make sense:
  • Single painting page: ← Gallery and a soft say hello link
  • About page: a say hello link where it feels natural in the text
  • Contact page: ← back link
  • Footer on every page: quiet links to Gallery · About · Contact
  • No hamburger menus, no dropdowns, no nav bar

This gives the site a considered, gallery-like feel rather than a website-like feel — which suits it perfectly.


Custom Theme — File Structure

shays-art/
├── style.css               # Theme header + base styles
├── functions.php           # Theme setup, enqueues, CPT registration, meta tags
├── index.php               # Fallback
├── front-page.php          # Gallery home page
├── single-painting.php     # Individual painting view
├── page.php                # Generic page template (About, Contact)
├── header.php              # Minimal header: site title only
├── footer.php              # Minimal footer with quiet nav links
└── assets/
    ├── css/
    │   └── main.css        # All styles (grid, zoom, responsive, dark theme)
    └── js/
        └── zoom.js         # Pinch/scroll zoom for painting view

Custom Post Type: painting

Registered in functions.php. Each painting has:

  • Title — shown at top of the single painting page
  • Featured Image — the painting itself (WordPress handles thumbnails natively)
  • Custom meta field: display_order — integer, so Shay can control the gallery sequence manually

No WooCommerce, no price fields, no stock. If someone wants to buy, they say hello.


Gallery Page (front-page.php)

Layout

Responsive CSS Grid — no JavaScript required for the layout itself.

.gallery-grid {
  display: grid;
  gap: 12px;
  padding: 12px;
  grid-template-columns: repeat(1, 1fr);   /* mobile: 1 column */
  background: #000;
}

@media (min-width: 600px) {
  .gallery-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 960px) {
  .gallery-grid { grid-template-columns: repeat(3, 1fr); }
}

Thumbnails

  • Each cell is a 1:1 square aspect-ratio box with background: #000
  • The painting image is centred inside using object-fit: contain — no cropping, no clipping
  • Portrait or landscape paintings float naturally in the square with black letterbox/pillarbox padding that is invisible against the black background — the painting just floats
  • Clicking a thumbnail navigates to that painting’s single page
  • WordPress generates thumbnails at a defined square size for fast loading; the contained display is handled by CSS

Query

Paintings ordered by display_order meta field (ascending), then by date as fallback. Shay can reorder by updating the number on each painting in the admin panel.


Single Painting Page (single-painting.php)

  • Title at the top in light type on black
  • Full image, as large as the screen allows, with:
  • Pinch-to-zoom on mobile (touch events via zoom.js, small vanilla JS — no library needed)
  • Scroll-to-zoom on desktop (mouse wheel)
  • The image never crops; it fits the screen and zooms from there
  • A soft “say hello” link below the image — goes to the Contact page, with the painting title quietly passed as a URL parameter so the form can pick it up
  • ← Gallery link to return home
  • Previous / Next painting navigation (easy to add, optional)

About Page

Standard WordPress page, styled to match. Light text on black. This page matters more than usual on a site like this — it’s where people decide if they want to reach out. Worth Shay putting real thought into what he writes here. No sidebar, no clutter.

A natural “say hello” link somewhere in or below the text.


Contact Page & Form

Custom Plugin: shays-contact

A simple dedicated plugin — approximately 100–150 lines of PHP. Replaces CF7 and Flamingo entirely. Benefits:

  • Zero third-party nag screens or dashboard widgets
  • Purpose-built: does exactly what’s needed and nothing else
  • First-class support for painting title pre-fill from URL parameter
  • Submissions saved to a custom database table (no lost emails)
  • Fully owned and understood code

Plugin file structure

shays-contact/
├── shays-contact.php       # Plugin header, form shortcode, submission handler
├── shays-contact-db.php    # DB table creation + submission logging
└── shays-contact-admin.php # Simple wp-admin page to view submissions

Form fields

  • Name — text
  • Email — email
  • About — text, pre-filled with painting title if coming from a painting page (via ?painting= URL param), otherwise blank or “General enquiry”
  • Message — textarea
  • Submit — “Send”

How the pre-fill works

On the single painting page, the “say hello” link points to /contact/?painting=Painting+Title. The contact form reads $_GET['painting'] and pre-fills the About field. No fuss.

On submission

  • Validates server-side (required fields, email format)
  • Saves to DB table wp_shays_contact_submissions
  • Sends email to Shay’s address
  • Shows a warm thank-you message — no redirect, no page reload

Anti-spam

  • Honeypot hidden field (bots fill it, humans don’t see it — submission silently discarded if filled)
  • No CAPTCHA unless spam becomes a problem

Tone

The form copy is warm and unpressured — “Say hello”, not “Submit enquiry”. The thank-you message is human.


Social Media Meta Tags (Open Graph + Twitter Card)

Handled entirely in functions.php — no SEO plugin needed.

For the Gallery / Home page:

<meta property="og:title" content="Shay's Art" />
<meta property="og:description" content="Original paintings by Shay." />
<meta property="og:image" content="[hero image — one painting chosen to represent the site]" />
<meta property="og:type" content="website" />
<meta name="twitter:card" content="summary_large_image" />

For individual painting pages (fully automatic):

<meta property="og:title" content="[Painting Title] — Shay's Art" />
<meta property="og:description" content="Original painting by Shay." />
<meta property="og:image" content="[painting's featured image URL, pulled automatically]" />
<meta property="og:type" content="article" />
<meta name="twitter:card" content="summary_large_image" />

Every painting shared on social media previews with that painting’s own image — no configuration needed per painting.

The hero image (for home/About/Contact) is just one painting nominated by Shay and set once in the theme.


WordPress Configuration & Hardening

Core Settings (wp-admin)

  • Set front page to the Gallery (Settings → Reading → Static front page)
  • Disable comments site-wide (Settings → Discussion)
  • Remove default widgets, unused menus
  • Set permalink structure to /%postname%/

wp-config.php additions

// Disable file editing from wp-admin (security)
define('DISALLOW_FILE_EDIT', true);

// Limit post revisions
define('WP_POST_REVISIONS', 3);

.htaccess additions

# Block XML-RPC
<Files xmlrpc.php>
  Order Deny,Allow
  Deny from all
</Files>

# Block direct access to wp-config.php
<Files wp-config.php>
  Order Deny,Allow
  Deny from all
</Files>

# Block access to sensitive files
<FilesMatch "\.(log|sql|bak|env)$">
  Order Deny,Allow
  Deny from all
</FilesMatch>

Brute Force / Login Protection

Plugin: Limit Login Attempts Reloaded (free)

  • Locks out an IP after N failed login attempts
  • Lightweight, no account required, no phoning home

Login hardening in functions.php:

// Hide WordPress version from source
remove_action('wp_head', 'wp_generator');

// Vague login errors (don't reveal valid usernames)
add_filter('login_errors', function() {
  return 'Login failed. Please try again.';
});

Optional but recommended: Change the login URL from /wp-admin using the free WPS Hide Login plugin. Stops the vast majority of automated attacks before they even try.


Plugins Summary

PluginPurposeWhy not in theme
shays-contact (custom)Contact form + submission logEmail/DB logic belongs in a plugin, not a theme
Limit Login Attempts ReloadedBrute force protectionSecurity independent of theme
WPS Hide Login (optional)Change login URLIndependent of theme

Total: 2–3 plugins. That’s it.


Image Handling Notes

  • Upload paintings at full resolution — WordPress generates smaller sizes automatically
  • Define a custom thumbnail size in functions.php: add_image_size('painting-thumb', 600, 600, false) — 600×600, no hard crop (so the full painting fits inside the square in CSS)
  • The full-size image is used on the single painting page for zoom
  • Recommend images are JPG at 85–90% quality for a good size/quality balance

Build Order

  1. Install WordPress
  2. Create theme folder with style.css and functions.php (minimum to activate)
  3. Register painting custom post type
  4. Build front-page.php + CSS grid
  5. Build single-painting.php + zoom JS
  6. Build header.php, footer.php, page.php
  7. Style everything (dark theme, typography, responsive)
  8. Add Open Graph meta tags to functions.php
  9. Build and activate shays-contact plugin
  10. Install Limit Login Attempts Reloaded
  11. Harden wp-config.php and .htaccess
  12. Add paintings and test on mobile and desktop
  13. Test social sharing (Facebook Sharing Debugger / Twitter Card Validator)

Open Questions for Shay

  • Do you want a site tagline or just “Shay’s Art”? (Used in the browser tab and meta descriptions), e.g., Intuitive Expressionism.
  • Any preferred ordering for the gallery, or happy with newest-first as the default?
  • Which painting should be the hero image for social sharing? (Only needed for the home page, About, and Contact—individual painting pages use the painting itself automatically)
  • Are you happy with your email address receiving contact form messages directly, or do you want a separate address set up first?
  • The About page will carry real weight on this site—worth drafting something you’re happy with before we build it, rather than dropping in placeholder text.