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
| Page | WordPress Type | Notes |
|---|---|---|
| Gallery (Home) | Custom template | Responsive thumbnail grid |
| Single Painting | Custom post type: painting | Zoomable image + title + soft contact link |
| About | Standard Page | Who Shay is — carries real weight on this kind of site |
| Contact | Standard Page | Simple, 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:
← Galleryand a softsay hellolink - About page: a
say hellolink where it feels natural in the text - Contact page:
← backlink - 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
← Gallerylink 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
| Plugin | Purpose | Why not in theme |
|---|---|---|
shays-contact (custom) | Contact form + submission log | Email/DB logic belongs in a plugin, not a theme |
| Limit Login Attempts Reloaded | Brute force protection | Security independent of theme |
| WPS Hide Login (optional) | Change login URL | Independent 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
- Install WordPress
- Create theme folder with
style.cssandfunctions.php(minimum to activate) - Register
paintingcustom post type - Build
front-page.php+ CSS grid - Build
single-painting.php+ zoom JS - Build
header.php,footer.php,page.php - Style everything (dark theme, typography, responsive)
- Add Open Graph meta tags to
functions.php - Build and activate
shays-contactplugin - Install Limit Login Attempts Reloaded
- Harden
wp-config.phpand.htaccess - Add paintings and test on mobile and desktop
- 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.