obsidian-formfire/styles.css
tolvitty c4cd228e87 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 <noreply@anthropic.com>
2026-02-13 13:22:26 +01:00

661 lines
14 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ==========================================================================
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;
}