The flow
Press the + button in the bottom-right corner, or open the same form as a tearsheet or a modal. Close it with unsaved changes to see the guard, or make the upload fail to see the error.
Markup
The floating button, then the sheet holding the post form. Listen for jewel:post and call waitUntil() with your upload.
HTML Copy
<body class="has-fab">
…
<button class="fab" type="button" aria-label="New post" data-sheet-open="post-sheet">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>
</button>
<dialog class="sheet sheet--bottom" id="post-sheet" data-component="sheet" aria-labelledby="post-sheet-title">
<div class="sheet__panel panel panel--content">
<header class="sheet__head">
<h2 class="sheet__title label" id="post-sheet-title">New post</h2>
<button class="btn btn--ghost btn--icon" type="button" data-sheet-close aria-label="Close">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.25" aria-hidden="true"><path d="M6 6l12 12M18 6 6 18"/></svg>
</button>
</header>
<div class="sheet__body">
<form class="form" id="post-form" data-component="form post-form" novalidate data-success="Posted." aria-labelledby="post-sheet-title">
<div class="field">
<label class="field__label" for="post-photo">Photo<span class="field__req" aria-hidden="true">*</span></label>
<div class="dropzone knockout" data-component="dropzone" data-max-size="20">
<input class="dropzone__input" id="post-photo" name="photo" type="file" accept="image/*" required>
<label class="dropzone__empty" for="post-photo">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.25" aria-hidden="true"><rect x="3" y="5" width="18" height="14"/><path d="m3 16 5-5 4 4 3-3 6 6"/><circle cx="16" cy="9" r="1.5"/></svg>
<span class="dropzone__title">Drop a photo, or <u>browse</u></span>
<span class="dropzone__hint">One photo · up to 20 MB</span>
</label>
<figure class="dropzone__preview" hidden><img alt=""><figcaption class="dropzone__meta"></figcaption></figure>
<div class="dropzone__actions" hidden>
<button class="btn btn--ghost" type="button" data-dropzone-replace>Replace</button>
<button class="btn btn--text" type="button" data-dropzone-remove>Remove</button>
</div>
</div>
</div>
<div class="field">
<label class="field__label" for="post-title">Title<span class="field__req" aria-hidden="true">*</span></label>
<input class="input" id="post-title" name="title" type="text" required maxlength="80" data-counter aria-describedby="post-title-count" placeholder="Name the photo">
<div class="field__meta"><p class="field__counter" id="post-title-count"></p></div>
</div>
<div class="field">
<label class="field__label" for="post-description">Description <span class="field__optional">(optional)</span></label>
<textarea class="input" id="post-description" name="description" rows="3" maxlength="500" data-counter aria-describedby="post-description-count" placeholder="Where, when, what it means"></textarea>
<div class="field__meta"><p class="field__counter" id="post-description-count"></p></div>
</div>
<div class="form__row form__row--2">
<div class="field">
<label class="field__label" for="post-location">Location <span class="field__optional">(optional)</span></label>
<input class="input" id="post-location" name="location" type="text" autocomplete="off" placeholder="Dayton, Ohio">
</div>
<div class="field">
<label class="field__label" for="post-tags">Tags <span class="field__optional">(up to 10)</span></label>
<div class="tag-input" data-component="tag-input" data-max="10" data-suggestions='["Dayton","Graffiti","Pond","Towers"]'>
<ul class="tag-input__list" aria-label="Tags"></ul>
<input class="tag-input__text" id="post-tags" type="text" placeholder="Add a tag">
<input type="hidden" name="tags">
</div>
</div>
</div>
<div class="field">
<label class="field__label" for="post-alt">Alt text <span class="field__optional">(optional)</span> <span class="field__badge" hidden>Suggested</span></label>
<textarea class="input" id="post-alt" name="alt" rows="2" aria-describedby="post-alt-hint" placeholder="Describe the photo for people who can’t see it"></textarea>
<p class="field__hint" id="post-alt-hint">Never blocks posting. A suggestion may appear here after you pick a photo; edit or clear it.</p>
</div>
<label class="choice"><input type="checkbox" role="switch" name="publish" checked> Publish to site</label>
<p class="form__error" role="alert" hidden></p>
<p class="form__status" role="status" hidden></p>
<div class="form__actions">
<button class="btn btn--text" type="button" data-sheet-close>Cancel</button>
<button class="btn" type="submit" data-busy-label="Posting…">Post</button>
</div>
</form>
</div>
</div>
</dialog>
<script>
document.getElementById('post-form').addEventListener('jewel:post', (e) => {
e.detail.waitUntil(upload(e.detail.formData)); // your code
});
</script>
</body>