136 lines
3.1 KiB
Vue
136 lines
3.1 KiB
Vue
<script setup>
|
|
import { computed, inject } from 'vue';
|
|
|
|
defineProps({
|
|
headline: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
subline: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
mobilePreview: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
});
|
|
|
|
const updateHeader = inject('editorUpdateHeader', null);
|
|
const isEditable = computed(() => !!updateHeader);
|
|
|
|
function onHeadlineBlur(e) {
|
|
updateHeader?.('headline', e.target.innerText.trim());
|
|
}
|
|
|
|
function onSublineBlur(e) {
|
|
updateHeader?.('subline', e.target.innerText.trim());
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<section class="project-headline" :class="{ 'project-headline--mobile': mobilePreview }">
|
|
<span class="project-headline__eyebrow">Project headline</span>
|
|
<h1
|
|
v-if="isEditable"
|
|
contenteditable="true"
|
|
class="project-headline__editable"
|
|
data-placeholder="Untitled project"
|
|
:class="{ 'project-headline__editable--empty': !headline }"
|
|
@blur="onHeadlineBlur"
|
|
v-text="headline || ''"
|
|
></h1>
|
|
<h1 v-else>{{ headline || 'Untitled project' }}</h1>
|
|
<p
|
|
v-if="isEditable"
|
|
contenteditable="true"
|
|
class="project-headline__subline project-headline__editable project-headline__subline--edit"
|
|
data-placeholder="Add a subline…"
|
|
@blur="onSublineBlur"
|
|
v-text="subline || ''"
|
|
></p>
|
|
<p v-else-if="subline" class="project-headline__subline">{{ subline }}</p>
|
|
</section>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.project-headline {
|
|
container-type: inline-size;
|
|
display: grid;
|
|
gap: 0.85rem;
|
|
}
|
|
|
|
.project-headline__eyebrow {
|
|
color: var(--project-muted, #6b7280);
|
|
font-size: 0.8rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.18em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
h1 {
|
|
color: var(--project-ink, #111827);
|
|
font-size: clamp(2.3rem, 5vw, 4.75rem);
|
|
font-weight: 700;
|
|
letter-spacing: -0.04em;
|
|
line-height: 0.94;
|
|
margin: 0;
|
|
max-width: 20ch;
|
|
}
|
|
|
|
.project-headline__subline {
|
|
color: var(--project-muted, #6b7280);
|
|
font-size: clamp(1rem, 1.5vw, 1.35rem);
|
|
font-weight: 400;
|
|
line-height: 1.5;
|
|
margin: 0;
|
|
max-width: 42ch;
|
|
}
|
|
|
|
/* ---- Inline edit ---- */
|
|
.project-headline__editable {
|
|
outline: none;
|
|
border-bottom: 1px solid transparent;
|
|
transition: border-color 0.15s;
|
|
}
|
|
|
|
.project-headline__editable:hover {
|
|
border-bottom-color: rgba(15, 23, 42, 0.18);
|
|
}
|
|
|
|
.project-headline__editable:focus {
|
|
border-bottom-color: rgba(14, 116, 144, 0.5);
|
|
}
|
|
|
|
.project-headline__editable:empty::before {
|
|
color: var(--project-muted, #6b7280);
|
|
content: attr(data-placeholder);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.project-headline__subline--edit {
|
|
min-height: 1.5em;
|
|
}
|
|
|
|
.project-headline--mobile h1 {
|
|
font-size: clamp(1.75rem, 7vw, 2.5rem);
|
|
max-width: 100%;
|
|
}
|
|
|
|
.project-headline--mobile .project-headline__subline {
|
|
font-size: 1rem;
|
|
max-width: 100%;
|
|
}
|
|
|
|
@container (max-width: 480px) {
|
|
h1 {
|
|
font-size: clamp(1.75rem, 9cqi, 2.5rem);
|
|
max-width: 100%;
|
|
}
|
|
|
|
.project-headline__subline {
|
|
font-size: 1rem;
|
|
max-width: 100%;
|
|
}
|
|
}
|
|
</style> |