From c4cd228e8788a840a8a3e87ffc0af9b1304f42fd Mon Sep 17 00:00:00 2001 From: tolvitty Date: Fri, 13 Feb 2026 13:22:26 +0100 Subject: [PATCH] feat: add Formfire styles with refined utilitarian design Production-grade CSS covering all UI components: form fields with focus states, tag chips with enter/exit animations, suggest list dropdowns, gold star rating with hover preview, form modal layout, form builder card-based field editor, and sidebar with mode badges. All colors via Obsidian CSS variables for light/dark theme support. Smooth transitions (0.15-0.25s ease) throughout. Co-Authored-By: Claude Opus 4.6 --- styles.css | 661 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 661 insertions(+) create mode 100644 styles.css diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..2df3acc --- /dev/null +++ b/styles.css @@ -0,0 +1,661 @@ +/* ========================================================================== + Formfire – Refined Utilitarian Styles + All colours use Obsidian CSS variables for light/dark theme compatibility. + ========================================================================== */ + +/* --------------------------------------------------------------------------- + Shake animation for validation errors + --------------------------------------------------------------------------- */ + +@keyframes ff-shake { + 0%, 100% { transform: translateX(0); } + 20% { transform: translateX(-6px); } + 40% { transform: translateX(6px); } + 60% { transform: translateX(-4px); } + 80% { transform: translateX(4px); } +} + +.ff-shake { + animation: ff-shake 0.4s ease; +} + +/* --------------------------------------------------------------------------- + Error reveal animation + --------------------------------------------------------------------------- */ + +@keyframes ff-error-reveal { + from { + opacity: 0; + transform: translateY(-4px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* --------------------------------------------------------------------------- + Tag chip enter animation + --------------------------------------------------------------------------- */ + +@keyframes ff-chip-enter { + from { + opacity: 0; + transform: scale(0.85); + } + to { + opacity: 1; + transform: scale(1); + } +} + +/* --------------------------------------------------------------------------- + Suggest list fade-in + --------------------------------------------------------------------------- */ + +@keyframes ff-fade-in { + from { opacity: 0; transform: translateY(-4px); } + to { opacity: 1; transform: translateY(0); } +} + + +/* ========================================================================== + Form Fields + ========================================================================== */ + +.ff-field { + margin-bottom: 12px; +} + +.ff-field-label { + display: block; + font-size: 0.875rem; + font-weight: 500; + color: var(--text-normal); + margin-bottom: 4px; + letter-spacing: 0.01em; +} + +.ff-field-required { + color: var(--interactive-accent); + font-weight: 600; +} + +.ff-field-control { + width: 100%; +} + +.ff-field-error { + font-size: 0.8rem; + color: var(--text-error, #e03e3e); + margin-top: 4px; + animation: ff-error-reveal 0.25s ease; +} + +.ff-field--error .ff-input, +.ff-field--error .ff-textarea, +.ff-field--error .ff-dropdown { + border-color: var(--text-error, #e03e3e); +} + +/* --- Inputs & textareas --- */ + +.ff-input, +.ff-textarea { + width: 100%; + padding: 6px 10px; + border: 1px solid var(--background-modifier-border); + border-radius: 4px; + background: var(--background-primary); + color: var(--text-normal); + font-size: 0.9rem; + font-family: inherit; + transition: border-color 0.15s ease, box-shadow 0.15s ease; + box-sizing: border-box; +} + +.ff-input:focus, +.ff-textarea:focus { + outline: none; + border-color: var(--interactive-accent); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--interactive-accent) 20%, transparent); +} + +.ff-textarea { + resize: vertical; + min-height: 80px; +} + +/* --- Dropdown --- */ + +.ff-dropdown { + width: 100%; + padding: 6px 10px; + border: 1px solid var(--background-modifier-border); + border-radius: 4px; + background: var(--background-primary); + color: var(--text-normal); + font-size: 0.9rem; + font-family: inherit; + transition: border-color 0.15s ease, box-shadow 0.15s ease; + box-sizing: border-box; + cursor: pointer; +} + +.ff-dropdown:focus { + outline: none; + border-color: var(--interactive-accent); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--interactive-accent) 20%, transparent); +} + + +/* ========================================================================== + Tags + ========================================================================== */ + +.ff-tags-container { + border: 1px solid var(--background-modifier-border); + border-radius: 4px; + padding: 6px 8px; + background: var(--background-primary); + display: flex; + flex-wrap: wrap; + gap: 6px; + align-items: center; + transition: border-color 0.15s ease, box-shadow 0.15s ease; + cursor: text; +} + +.ff-tags-container:focus-within { + border-color: var(--interactive-accent); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--interactive-accent) 20%, transparent); +} + +.ff-tags-chips { + display: contents; +} + +.ff-tag-chip { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 2px 8px; + border-radius: 12px; + background: var(--background-secondary); + color: var(--text-normal); + font-size: 0.8rem; + line-height: 1.4; + animation: ff-chip-enter 0.2s ease; + transition: background-color 0.15s ease; +} + +.ff-tag-chip:hover { + background: var(--background-modifier-hover); +} + +.ff-tag-text { + max-width: 160px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.ff-tag-remove { + cursor: pointer; + color: var(--text-muted); + font-size: 0.9rem; + line-height: 1; + opacity: 0; + transition: opacity 0.15s ease, color 0.15s ease; + margin-left: 2px; +} + +.ff-tag-chip:hover .ff-tag-remove { + opacity: 1; +} + +.ff-tag-remove:hover { + color: var(--text-error, #e03e3e); +} + +.ff-tags-input { + flex: 1 1 80px; + min-width: 80px; + border: none; + background: transparent; + color: var(--text-normal); + font-size: 0.85rem; + font-family: inherit; + outline: none; + padding: 2px 0; +} + + +/* ========================================================================== + Suggest List (note-link, folder-picker) + ========================================================================== */ + +.ff-suggest-wrapper { + position: relative; + width: 100%; +} + +.ff-suggest-list { + position: absolute; + z-index: var(--layer-popover, 30); + top: 100%; + left: 0; + right: 0; + max-height: 200px; + overflow-y: auto; + margin-top: 2px; + border: 1px solid var(--background-modifier-border); + border-radius: 4px; + background: var(--background-primary); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12); + animation: ff-fade-in 0.15s ease; +} + +.ff-suggest-item { + padding: 6px 10px; + font-size: 0.85rem; + color: var(--text-normal); + cursor: pointer; + transition: background-color 0.1s ease; +} + +.ff-suggest-item:hover { + background: var(--background-modifier-hover); +} + + +/* ========================================================================== + Rating Stars + ========================================================================== */ + +.ff-rating { + display: flex; + gap: 4px; + padding: 4px 0; +} + +.ff-star { + font-size: 22px; + cursor: pointer; + color: var(--text-faint); + transition: color 0.15s ease, transform 0.2s ease; + user-select: none; + line-height: 1; +} + +.ff-star:hover { + transform: scale(1.15); +} + +.ff-star-active { + color: #e6a817; +} + +.ff-star-hover { + color: #f0c040; +} + + +/* ========================================================================== + Form Modal + ========================================================================== */ + +.ff-form-modal { + max-width: 500px; + margin: 0 auto; +} + +.ff-form-title { + margin: 0 0 4px 0; + font-size: 1.25rem; + font-weight: 600; + color: var(--text-normal); +} + +.ff-form-target { + font-size: 0.8rem; + color: var(--text-muted); + margin-bottom: 16px; + padding: 4px 8px; + background: var(--background-secondary); + border-radius: 4px; +} + +.ff-fields { + margin-bottom: 16px; + max-height: 60vh; + overflow-y: auto; + padding-right: 4px; +} + +.ff-form-footer { + display: flex; + justify-content: flex-end; + padding-top: 12px; + border-top: 1px solid var(--background-modifier-border); +} + +.ff-submit-btn { + min-width: 120px; +} + + +/* ========================================================================== + Form Picker Modal — inherits Obsidian's FuzzySuggestModal styles + ========================================================================== */ + +/* No custom overrides needed; the native modal styles are sufficient. */ + + +/* ========================================================================== + Sidebar + ========================================================================== */ + +.ff-sidebar { + padding: 8px 0; +} + +.ff-sidebar-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 4px 12px 8px; + border-bottom: 1px solid var(--background-modifier-border); + margin-bottom: 4px; +} + +.ff-sidebar-title { + font-size: 0.9rem; + font-weight: 600; + color: var(--text-normal); + letter-spacing: 0.02em; +} + +.ff-sidebar-settings { + cursor: pointer; + color: var(--text-muted); + display: flex; + align-items: center; + justify-content: center; + padding: 4px; + border-radius: 4px; + transition: color 0.15s ease, background-color 0.15s ease; +} + +.ff-sidebar-settings:hover { + color: var(--text-normal); + background: var(--background-modifier-hover); +} + +.ff-sidebar-list { + padding: 4px 0; +} + +.ff-sidebar-item { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 12px; + cursor: pointer; + border-left: 3px solid transparent; + transition: background-color 0.15s ease, border-color 0.2s ease; +} + +.ff-sidebar-item:hover { + background: var(--background-modifier-hover); + border-left-color: var(--interactive-accent); +} + +.ff-sidebar-badge { + display: inline-flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + border-radius: 4px; + font-size: 0.7rem; + font-weight: 700; + flex-shrink: 0; + line-height: 1; +} + +.ff-badge-create { + background: color-mix(in srgb, var(--interactive-accent) 18%, transparent); + color: var(--interactive-accent); +} + +.ff-badge-update { + background: color-mix(in srgb, #4caf50 18%, transparent); + color: #4caf50; +} + +.ff-sidebar-name { + font-size: 0.85rem; + color: var(--text-normal); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.ff-sidebar-empty { + padding: 24px 12px; + text-align: center; + font-size: 0.85rem; + color: var(--text-muted); + line-height: 1.5; +} + + +/* ========================================================================== + Form Builder Modal + ========================================================================== */ + +.ff-builder-modal { + max-width: 700px; + margin: 0 auto; +} + +.ff-builder-modal h2 { + margin: 0 0 12px 0; + font-size: 1.2rem; + font-weight: 600; + color: var(--text-normal); +} + +.ff-builder-modal h3 { + margin: 20px 0 8px 0; + font-size: 1rem; + font-weight: 600; + color: var(--text-normal); +} + +.ff-builder-general { + margin-bottom: 8px; +} + +/* --- Setting row --- */ + +.ff-builder-setting { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 8px; +} + +.ff-builder-setting-label { + flex: 0 0 140px; + font-size: 0.85rem; + color: var(--text-muted); + text-align: right; +} + +.ff-builder-setting .ff-input, +.ff-builder-setting .ff-dropdown { + flex: 1; +} + +.ff-builder-textarea { + flex: 1; + width: 100%; + padding: 6px 10px; + border: 1px solid var(--background-modifier-border); + border-radius: 4px; + background: var(--background-primary); + color: var(--text-normal); + font-size: 0.85rem; + font-family: var(--font-monospace); + resize: vertical; + min-height: 70px; + transition: border-color 0.15s ease, box-shadow 0.15s ease; + box-sizing: border-box; +} + +.ff-builder-textarea:focus { + outline: none; + border-color: var(--interactive-accent); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--interactive-accent) 20%, transparent); +} + +/* --- Fields list --- */ + +.ff-builder-fields { + display: flex; + flex-direction: column; + gap: 8px; + max-height: 45vh; + overflow-y: auto; + padding-right: 4px; +} + +.ff-builder-field { + border: 1px solid var(--background-modifier-border); + border-radius: 6px; + background: var(--background-secondary); + overflow: hidden; + transition: border-color 0.15s ease, box-shadow 0.15s ease; +} + +.ff-builder-field:hover { + border-color: color-mix(in srgb, var(--interactive-accent) 40%, var(--background-modifier-border)); +} + +.ff-builder-field-header { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 10px; + background: color-mix(in srgb, var(--background-secondary) 80%, var(--background-primary)); + border-bottom: 1px solid var(--background-modifier-border); +} + +.ff-builder-field-num { + font-size: 0.75rem; + font-weight: 700; + color: var(--text-faint); + font-variant-numeric: tabular-nums; +} + +.ff-builder-field-preview { + flex: 1; + font-size: 0.85rem; + font-weight: 500; + color: var(--text-normal); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.ff-builder-field-actions { + display: flex; + gap: 4px; + flex-shrink: 0; +} + +.ff-builder-action-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + border: none; + border-radius: 4px; + background: transparent; + color: var(--text-muted); + font-size: 0.85rem; + cursor: pointer; + transition: background-color 0.15s ease, color 0.15s ease; + padding: 0; +} + +.ff-builder-action-btn:hover { + background: var(--background-modifier-hover); + color: var(--text-normal); +} + +.ff-builder-delete:hover { + background: color-mix(in srgb, var(--text-error, #e03e3e) 12%, transparent); + color: var(--text-error, #e03e3e); +} + +.ff-builder-field-body { + padding: 10px; +} + +/* --- Add field button --- */ + +.ff-builder-add-btn { + display: block; + width: 100%; + margin-top: 8px; + padding: 10px; + border: 2px dashed var(--background-modifier-border); + border-radius: 6px; + background: transparent; + color: var(--text-muted); + font-size: 0.85rem; + cursor: pointer; + text-align: center; + transition: border-color 0.15s ease, color 0.15s ease, background-color 0.15s ease; +} + +.ff-builder-add-btn:hover { + border-color: var(--interactive-accent); + color: var(--interactive-accent); + background: color-mix(in srgb, var(--interactive-accent) 5%, transparent); +} + +/* --- Footer --- */ + +.ff-builder-footer { + display: flex; + justify-content: flex-end; + gap: 8px; + margin-top: 16px; + padding-top: 12px; + border-top: 1px solid var(--background-modifier-border); +} + +/* --- Settings tab empty state --- */ + +.ff-settings-empty { + padding: 32px 16px; + text-align: center; + font-size: 0.9rem; + color: var(--text-muted); + line-height: 1.6; +} + + +/* ========================================================================== + Toggle (Obsidian-native) – ensure consistent sizing in our forms + ========================================================================== */ + +.ff-builder-setting .checkbox-container, +.ff-field-control .checkbox-container { + flex-shrink: 0; +}