← back to Stars of Design
views/public/designer.ejs
135 lines
<%- include('../partials/head', { title, meta_desc_override: typeof meta_desc_override !== 'undefined' ? meta_desc_override : undefined }) %>
<%
// JSON-LD Person schema for richer SERP cards. Mention=DW collections that
// pair with this designer; affiliation/image kept loose since we host neither
// images nor employment data.
var jsonld = {
'@context': 'https://schema.org',
'@type': 'Person',
name: designer.name,
description: designer.bio || designer.headline || ('Interior designer profile on Stars of Design.'),
url: 'https://starsofdesign.com/designers/' + designer.slug,
jobTitle: 'Interior Designer',
knowsAbout: (designer.styles || []).slice(0, 8),
};
if (designer.city) {
jsonld.workLocation = { '@type': 'Place', name: designer.city };
}
if (designer.preferred_dw && designer.preferred_dw.length) {
jsonld.subjectOf = designer.preferred_dw.map(function (p) {
return { '@type': 'CreativeWork', name: p.label, url: p.url };
});
}
%>
<%
// Escape </script>, HTML comment markers, and U+2028/U+2029 so the JSON-LD
// payload can't break out of the <script> tag if any data field ever contains
// those substrings. Standard OWASP XSS Prevention Rule #3.1 for inline JSON.
var jsonldSafe = JSON.stringify(jsonld)
.replace(/<\/script/gi, '<\\/script')
.replace(/<!--/g, '<\\!--')
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
%>
<script type="application/ld+json"><%- jsonldSafe %></script>
<%- include('../partials/header') %>
<main class="designer-detail">
<section class="section">
<div class="wrap narrow">
<p class="muted"><a href="/designers">← Back to all designers</a></p>
<header class="detail-header">
<span class="detail-mark" aria-hidden="true"><%= (designer.name || '?').split(' ').slice(0,2).map(function (w) { return w[0]; }).join('') %></span>
<div>
<h1><%= designer.name %></h1>
<% if (designer.headline) { %><p class="lede"><%= designer.headline %></p><% } %>
<p class="muted">
<% if (designer.era) { %><%= designer.era %><% } %>
<% if (designer.active_years) { %> · <%= designer.active_years %><% } %>
<% if (designer.city) { %> · <%= designer.city %><% } %>
</p>
</div>
</header>
<% if (designer.styles && designer.styles.length) { %>
<h2>Signature styles</h2>
<ul class="pills">
<% designer.styles.forEach(function (s) { %><li><%= s %></li><% }) %>
</ul>
<% } %>
<% if (designer.bio) { %>
<h2>Profile</h2>
<p><%= designer.bio %></p>
<% } %>
<% if (designer.signature) { %>
<h2>Visual language</h2>
<p><%= designer.signature %></p>
<% } %>
<% if (designer.preferred_dw && designer.preferred_dw.length) { %>
<h2>Wallcoverings that match this designer</h2>
<p class="muted small">Curated by Designer Wallcoverings — collections that map to <%= designer.name.split(' ')[0] %>'s published interiors.</p>
<div class="pairing-grid">
<% designer.preferred_dw.forEach(function (p) { %>
<a class="pairing-card" href="<%= p.url %>" rel="noopener noreferrer" target="_blank">
<span class="pairing-label"><%= p.label %></span>
<span class="pairing-arrow" aria-hidden="true">→</span>
</a>
<% }); %>
</div>
<% } %>
<% if (typeof designerVideos !== 'undefined' && designerVideos.length) { %>
<h2>Watch <%= designer.name.split(' ').slice(0, 2).join(' ') %></h2>
<p class="muted small">Home tours, interviews, and project showcases on YouTube.</p>
<div class="profile-video-grid">
<% designerVideos.forEach(function (v) { %>
<a class="profile-video-card" href="<%= v.url %>" target="_blank" rel="noopener noreferrer" aria-label="<%= v.title %> — opens YouTube in new tab">
<span class="profile-video-poster" style="background-image:url('<%= v.thumb_url %>')">
<span class="profile-video-play" aria-hidden="true">▶</span>
</span>
<span class="profile-video-meta">
<strong><%= v.title %></strong>
<span class="muted small"><%= v.channel %><% if (v.year) { %> · <%= v.year %><% } %></span>
</span>
</a>
<% }); %>
</div>
<% } %>
<% if (designer.firm_url || designer.instagram_url) { %>
<h2>Find the firm</h2>
<ul class="links">
<% if (designer.firm_url) { %><li><a href="<%= designer.firm_url %>" rel="noopener noreferrer" target="_blank">Firm website ↗</a></li><% } %>
<% if (designer.instagram_url) { %><li><a href="<%= designer.instagram_url %>" rel="noopener noreferrer" target="_blank">Instagram ↗</a></li><% } %>
</ul>
<% } %>
<% if (designer.sources && designer.sources.length) { %>
<h2>Sources & press</h2>
<p class="muted small">External editorial coverage we cited when building this profile. Credits in line with fair-use editorial citation.</p>
<ul class="sources">
<% designer.sources.forEach(function (s) { %>
<li>
<a href="<%= s.url %>" rel="noopener noreferrer" target="_blank"><strong><%= s.outlet %></strong></a><% if (s.title) { %> — <em><%= s.title %></em><% } %><% if (s.year) { %> <span class="muted">(<%= s.year %>)</span><% } %>
</li>
<% }); %>
</ul>
<% } %>
<p class="muted small detail-disclaim">
Stars of Design is an editorial profile directory. We are not affiliated with <%= designer.name %> or
their studio. Wallcovering pairings are curated based on publicly available work and are sold by
<a href="https://designerwallcoverings.com" rel="noopener noreferrer">Designer Wallcoverings</a>, the directory's
publisher. Source citations are provided in line with fair-use editorial practice; visit each outlet
directly for the full article.
</p>
</div>
</section>
</main>
<%- include('../partials/footer') %>