2026-06-10 09:15:42 +00:00
/* tugtile — markdown kanban (prototype, read-only renderer) */
2026-06-13 03:01:42 +00:00
/ * Squircle ( continuous-curvature ) corners — progressive enhancement .
corner-shape is Chromium-only as of mid-2026 ; Safari / WebKit & Firefox ignore it and keep the
border-radius arc , so this is ZERO visual regression . Requires a non-zero border-radius ( still
set per-surface below ) . Token lives here because corner-shape is NOT inherited but a custom
property IS . Applied only to finite-radius card / button / panel / input surfaces — never pills / circles . * /
: root { --corner : squircle ; }
: where (
. tugtile-brand , . tugtile__lane , . tugtile__tile , . tugtile__num , . tugtile__popup ,
2026-06-22 03:25:41 +00:00
. tugtile__popup-d , . tugtile__popup-t , . tugtile__add-btn ,
. tugtile-prompt-field , . tugtile-iconbtn , . tugtile-ed-find-i ,
. tugtile-viewcycle , . tugtile__archivebar , . tugtile__lane-chevron ,
2026-06-13 03:01:42 +00:00
. tugtile-archive-row , . tugtile__addcol-btn
) { corner-shape : var ( - - corner ) ; }
2026-06-10 09:15:42 +00:00
. tugtile {
height : 100 % ;
padding : 0 ;
position : relative ; /* Contain the view-swap snapshot overlay */
}
/* Search/Undo/Redo live in the native back/forward container (repurposed by setupLeftActions), so they line up with the left-sidebar toggle above. */
. tugtile-leftacts { display : flex ; align-items : center ; gap : 2 px ; padding-left : 4 px ; } /* Nudge right to line up the first icon with the left-sidebar toggle above (auto-measure couldn't find the toggle on iPad) */
2026-06-22 03:25:41 +00:00
/ * iPad only , Obsidian sets ` . is-mobile . view-action { margin : auto 0 } ` ( specificity ( 0 , 2 , 0 ) ) on these addAction
icons , which misaligns Search / Undo / Redo . This ( 0 , 3 , 0 ) compound out-specifies it and resets the margin without
! important — the children always carry both classes ( addAction → . clickable-icon . view-action ) . * /
. tugtile-leftacts > . clickable-icon . view-action { margin : 0 ; }
2026-06-10 09:15:42 +00:00
/* Board⇄table swap: the outgoing snapshot slides up & out while the new view rises from below (two panels shuffling). */
. tugtile-viewsnap {
position : absolute ; inset : 0 ; z-index : 3 ;
background : var ( --background-primary ) ; overflow : hidden ; pointer-events : none ;
animation : tugtile-view-out 0 . 38s cubic-bezier ( . 7 , 0 , . 3 , 1 ) forwards ; /* ease-in-out: slow → fast → slow */
}
@ keyframes tugtile-view-out { from { transform : translateY ( 0 ) ; } to { transform : translateY ( -100 % ) ; } }
. tugtile--switching . tugtile__board ,
. tugtile--switching . tugtile__table-wrap { animation : tugtile-view-in 0.38 s cubic-bezier ( .7 , 0 , .3 , 1 ) ; }
@ keyframes tugtile-view-in { from { transform : translateY ( 100 % ) ; } to { transform : translateY ( 0 ) ; } }
/* Phone: the centered control (view-cycle · lock) sits in a top-bar in the content, not the cramped header */
/ * Solid bg + z-index above the view-swap snapshot ( z-index 3 ) : the bar stays put on top while board / table
slide beneath it , so the up-slide animation never leaks through the control row . * /
. tugtile__ctlbar { flex : 0 0 auto ; width : 100 % ; box-sizing : border-box ; display : flex ; justify-content : center ; padding : 2 px 8 px 8 px ; position : relative ; z-index : 4 ; background : var ( - - background - primary ) ; }
/ * Phone : kill the view-content top padding so the control bar sits flush against the header — otherwise that
strip sits ABOVE the bar and the view-swap snapshot ( which covers the whole content ) leaks through it . * /
body . is-phone . workspace-leaf-content [ data-type = "tugtile-board" ] . view-content { padding : 0 ; } /* All sides: the left/right padding inset the content so the full-width bar still left gaps on both edges */
/* Greyed-out (disabled-looking) header action — used for undo/redo when their stack is empty, instead of hiding the button */
. tugtile-act-off { opacity : 0.35 ; pointer-events : none ; }
/* Dimmed brand suffix appended to the centered header title: "<file name> · tugtile-ing" */
/* Centered header controls: [view-cycle] · [lock]. Sized up (font-size) to roughly match the right-hand toolbar icons. */
. tugtile-headerctl {
display : inline-flex ; align-items : center ; gap : 6px ; vertical-align : middle ;
font-size : 18px ;
}
. tugtile-sep { color : var ( - - text - faint ) ; } /* Neutral separator, NOT a button — equal button padding + the flex gap keep its two sides symmetric */
/* Brand suffix + lock icon = one button (t('brandSuffix') + padlock) that toggles the read-only lock */
. tugtile-brand {
display : inline-flex ; align-items : center ; gap : 5px ;
color : var ( --text-faint ) ; font-weight : var ( --font-normal ) ;
cursor : pointer ; border-radius : var ( --radius-s ) ; padding : 2px 6px ;
}
. tugtile-brand : hover { background : var ( - - background - modifier - hover ) ; color : var ( - - text - muted ) ; }
2026-06-13 03:01:42 +00:00
/ * TileEditModal ( tugtile 's big editor) control strip — scoped to the modal so marktile' s own strip is untouched .
A uniform gray bar spanning the full width : ✕ pinned far left , ✓ far right , the mode · lock centered between . * /
. tugtile-edit-modal-full . tugtile__ctlbar { background : var ( - - background - secondary ) ; }
. tugtile-edit-modal-full . tugtile-headerctl { display : flex ; width : 100 % ; }
. tugtile-edit-modal-full . tugtile-headerctl > . tugtile-iconbtn : first-child { margin-right : auto ; } /* ✕ → far left */
. tugtile-edit-modal-full . tugtile-headerctl > . tugtile-iconbtn : last-child { margin-left : auto ; } /* ✓ → far right */
2026-06-10 09:15:42 +00:00
. tugtile-lock-icon { display : inline-flex ; }
. tugtile-lock-icon svg { width : 0.9 em ; height : 0.9 em ; }
/* Locked board (read-only): hide add/edit affordances, disable checkboxes, drop drag cursors. View-only actions (collapse, view switch, search) still work. */
. tugtile--locked . tugtile__add ,
. tugtile--locked . tugtile__addcol ,
. tugtile--locked . tugtile__tile-more ,
. tugtile--locked . tugtile__lane-more { display : none ; }
. tugtile--locked . tugtile__check ,
. tugtile--locked . tugtile__row-title input { pointer-events : none ; }
. tugtile--locked . tugtile__tile ,
. tugtile--locked . tugtile__lane-head { cursor : default ; }
. tugtile__empty {
padding : 16px ;
color : var ( --text-muted ) ;
}
. tugtile__board {
display : flex ;
gap : 12px ;
align-items : flex-start ;
height : 100 % ;
overflow-x : auto ;
padding : 12px ;
}
. tugtile__lane {
flex : 0 0 auto ;
width : var ( --tugtile-lane-width , 300px ) ; /* Uniform fixed column width (configurable in settings); all lanes line up evenly */
max-height : 100 % ;
display : flex ;
flex-direction : column ;
background : var ( --background-secondary ) ;
border-radius : var ( --radius-m ) ;
padding : 8px ;
overflow : hidden ; /* Cleanly clips content into the changing lane width during collapse/expand (dragging uses a clone on body and is unaffected) */
--tg-d1 : 0 . 26s ; /* Stage 1: lane narrows + horizontal title tucks into the chevron (collapse) / widens + title grows back (expand) */
--tg-d2 : 0 . 24s ; /* Stage 2: vertical title grows down out of the chevron (collapse) / retracts up into it (expand) */
transition : width var ( --tg-d1 ) cubic-bezier ( . 4 , 0 , . 2 , 1 ) ; /* Smooth width change on collapse/expand */
}
. tugtile__lane-head {
display : flex ;
align-items : center ;
justify-content : space-between ;
gap : 8px ;
padding : 4px 0 8px ; /* Horizontal 0 → header rails line up with the card column (cards sit at the lane's own 8px padding on both sides) */
font-weight : 700 ;
cursor : grab ; /* Header acts as the drag handle for the lane */
}
. tugtile__lane-head : active { cursor : grabbing ; }
. tugtile__lane--ghost { opacity : 0.4 ; }
. tugtile__lane--chosen { box-shadow : 0 0 0 2 px var ( - - interactive - accent ) ; }
. tugtile__lane--drag { /* Slightly tilts and lifts the lane when dragging the entire column */
rotate : 2deg ;
box-shadow : 0 16px 36px rgba ( 0 , 0 , 0 , 0 . 5 ) ;
}
. tugtile__lane-count {
flex : 0 0 auto ;
opacity : 0 . 55 ;
font-size : 0 . 85em ;
transition : opacity var ( --tg-d1 ) ease ;
}
. tugtile__lane-count--over { /* WIP: Warning indicator when exceeding limits */
color : var ( --text-error ) ;
opacity : 1 ;
font-weight : 700 ;
}
. tugtile--hide-counts . tugtile__lane-count {
display : none ;
}
. tugtile__list {
display : flex ;
flex-direction : column ;
gap : 8px ;
overflow-y : auto ;
/* Scrollbar gutter without changing the card layout: the list extends 8px to the right (eating the lane's right padding) via the negative margin, while padding-right keeps the cards in their original position. The iOS/iPadOS overlay scrollbar then sits in that right padding instead of on top of the cards. */
margin-right : -8px ;
padding-right : 8px ;
}
2026-06-14 09:58:50 +00:00
. tugtile__tile : focus-visible { outline : none ; box-shadow : inset 0 0 0 2 px var ( - - interactive - accent ) ; } /* keyboard-focused card (arrow keys move it) */
2026-06-10 09:15:42 +00:00
. tugtile__tile {
position : relative ;
background : var ( --background-primary ) ;
border : 1px solid var ( --background-modifier-border ) ;
border-radius : var ( --radius-m ) ;
padding : 8px ;
cursor : grab ;
}
. tugtile__tile : active {
cursor : grabbing ;
}
. tugtile__tile : hover {
border-color : var ( --background-modifier-border-hover , var ( --interactive-accent ) ) ;
}
/* Dragging state (corresponding to the Sortable class in main.js) */
/* Placeholder left behind: faded out with a dashed border, similar to a Trello card slot */
. tugtile__tile--ghost {
opacity : 0 . 4 ;
background : var ( --background-modifier-hover ) ;
border-style : dashed ;
}
. tugtile__tile--ghost > * { visibility : hidden ; }
. tugtile__tile--chosen {
box-shadow : inset 0 0 0 2px var ( --interactive-accent ) ; /* Inner ring — an outer ring's left/right 2px gets clipped by the list's overflow (cards are full-width with no horizontal room) */
}
/ * The card following the cursor feels "picked up" : slightly tilted , scaled up , with a floating shadow .
Uses standalone rotate / scale properties ( not transform ) to avoid overwriting translations applied by Sortable . * /
. tugtile__tile--drag {
opacity : 0 . 97 ;
rotate : 3deg ;
scale : 1 . 03 ;
box-shadow : 0 14px 32px rgba ( 0 , 0 , 0 , 0 . 5 ) ;
cursor : grabbing ;
}
/ * Index number : a rounded inline chip prepended to the card text .
Key detail : keeping font-size unscaled ( matching body text size ) aligns the number and adjacent text to the same baseline ,
achieving natural vertical alignment without magic numbers , font , line-height , or scale issues . The background chip sits snug
around the number ( slight horizontal , minimal vertical padding ) , centering itself on the first line and wrapping naturally . * /
. tugtile__num {
display : inline-block ;
margin-right : 0 . 4em ;
padding : 0 0 . 4em ;
background : var ( --interactive-accent ) ;
color : var ( --text-on-accent ) ;
border-radius : var ( --radius-s ) ;
font-weight : 700 ;
line-height : 1 . 25 ;
}
/* Floating control area in the top-right: checkbox + ⋯. Using float allows the first line of text to wrap around it, keeping content aligned to the top without wasting space. */
. tugtile__tile-ctrls {
float : right ;
display : flex ;
align-items : center ;
gap : 6px ;
margin : 0 0 2px 8px ;
}
. tugtile__tile-body {
padding-top : 0 . 15em ; /* Subtle top padding for breathing room (kept smaller than the control height so the first line still wraps around it) */
overflow-wrap : anywhere ; /* Alphanumeric text/URLs do not overflow the lane */
contain : layout paint ; /* The body's internal layout is self-contained, so animating the tile's height doesn't re-flow the rendered markdown every frame — kills the stutter on content-heavy tiles */
}
/* Long card collapse: hides the full text when folded, showing only the first line of the title (Approach C, without clipping) */
. tugtile__tile-title {
display : none ;
padding-top : 0 . 15em ;
font-weight : 600 ;
}
. tugtile__tile-title > : first-child { margin-top : 0 ; }
. tugtile__tile-title > : last-child { margin-bottom : 0 ; }
. tugtile__tile--folded . tugtile__tile-body { display : none ; }
. tugtile__tile--folded . tugtile__tile-title { display : block ; }
/ * On expand the tile height snaps ( one reflow ) , then the body slides + fades in on the GPU — no per-frame
layout , so it stays smooth no matter how much content the tile holds . * /
. tugtile__tile--revealing . tugtile__tile-body { animation : tugtile-body-reveal 0.24 s cubic-bezier ( .4 , 0 , .2 , 1 ) both ; }
@ keyframes tugtile-body-reveal {
from { opacity : 0 ; transform : translateY ( -6 px ) ; }
to { opacity : 1 ; transform : none ; }
} . tugtile__tile-body > : first-child {
margin-top : 0 ;
}
. tugtile__tile-body > : last-child {
margin-bottom : 0 ;
}
/* Date and time picker popup */
. tugtile__popup {
position : fixed ;
z-index : 9999 ;
background : var ( --background-primary ) ;
border : 1px solid var ( --background-modifier-border ) ;
border-radius : var ( --radius-m ) ;
box-shadow : var ( --shadow-l ) ;
padding : 8px ;
user-select : none ;
}
. tugtile__popup-head {
display : flex ;
align-items : center ;
justify-content : space-between ;
font-weight : 600 ;
padding : 2px 4px 8px ;
}
. tugtile__popup-nav {
cursor : pointer ;
padding : 0 8px ;
display : inline-flex ;
align-items : center ;
}
. tugtile__popup-nav svg { width : 1.15 em ; height : 1.15 em ; }
. tugtile__popup-nav : hover { color : var ( - - text - accent ) ; }
. tugtile__popup-grid {
display : grid ;
grid-template-columns : repeat ( 7 , 2em ) ;
gap : 2px ;
text-align : center ;
}
. tugtile__popup-w { opacity : 0.5 ; font-size : 0.8 em ; }
. tugtile__popup-d {
cursor : pointer ;
border-radius : var ( --radius-s ) ;
padding : 3px 0 ;
line-height : 1 . 4 ;
}
. tugtile__popup-d : hover { background : var ( - - background - modifier - hover ) ; }
. tugtile__popup-d--today { color : var ( - - text - accent ) ; font-weight : 700 ; }
. tugtile__popup--time {
max-height : 240px ;
overflow-y : auto ;
padding : 4px ;
}
. tugtile__popup-t {
cursor : pointer ;
padding : 4px 16px ;
border-radius : var ( --radius-s ) ;
}
. tugtile__popup-t : hover { background : var ( - - background - modifier - hover ) ; }
/* Tag chips at the bottom of the card (when move-tags-to-card-footer is enabled) */
. tugtile__tags {
margin-top : 6px ;
display : flex ;
flex-wrap : wrap ;
gap : 4px ;
}
. tugtile__tag {
font-size : 0 . 78em ;
padding : 1px 7px ;
border-radius : 999px ;
background : var ( --background-modifier-hover ) ;
color : var ( --text-accent ) ;
cursor : pointer ;
}
. tugtile__tag : hover { filter : brightness ( 1.1 ) ; }
/* Date/time chips at the bottom of the card */
. tugtile__date {
margin-top : 6px ;
font-size : 0 . 78em ;
color : var ( --text-muted ) ;
display : flex ;
flex-wrap : wrap ;
align-items : center ;
2026-06-14 02:31:09 +00:00
gap : 8px ; /* separates the date and time chips (replaces the old full-width-space spacer) */
2026-06-10 09:15:42 +00:00
}
2026-06-14 02:31:09 +00:00
. tugtile__date-d , . tugtile__date-t { display : inline-flex ; align-items : center ; gap : 3 px ; }
. tugtile__date svg { width : 1 em ; height : 1 em ; } /* Lucide calendar/clock, sized to the chip text */
. tugtile__archivebar-i { display : inline-flex ; }
. tugtile__archivebar svg { width : 1.1 em ; height : 1.1 em ; } /* Lucide archive icon (was a 📥 emoji) */
2026-06-10 09:15:42 +00:00
/* Date status colors: red for overdue, accent for today, yellow for upcoming (≤7 days), and green for safe (>7 days); colored whenever a date exists */
. tugtile__date--overdue { color : var ( - - color - red ) ; font-weight : 600 ; }
. tugtile__date--today { color : var ( - - text - accent ) ; font-weight : 600 ; }
. tugtile__date--soon { color : var ( - - color - yellow ) ; font-weight : 600 ; }
. tugtile__date--safe { color : var ( - - color - green ) ; }
. tugtile__date--unknown { color : var ( - - text - muted ) ; } /* Neutral color used when the date cannot be parsed */
/* Freeze container scrolling during dragging (adding .is-dragging in main.js onStart) to prevent native mobile scrolling from fighting the drag gesture */
. tugtile . is-dragging . tugtile__board ,
. tugtile . is-dragging . tugtile__list {
2026-06-22 03:25:41 +00:00
/* (0,2,0) already out-specifies the base .tugtile__board/__list (0,1,0) in this file, so no !important needed */
overflow : hidden ;
touch-action : none ;
2026-06-10 09:15:42 +00:00
}
/* Add card button and input */
. tugtile__add {
padding-top : 8px ; /* Match the 8px gap between cards so the add button keeps the same vertical rhythm */
}
. tugtile__add-btn {
width : 100 % ;
text-align : left ;
background : transparent ;
border : 1px dashed var ( --background-modifier-border ) ;
border-radius : var ( --radius-m ) ;
color : var ( --text-muted ) ;
padding : 8px ; /* Match card padding and the add-lane button (consistent 8px rhythm); left-aligned "+" lines up with the card number chip */
cursor : pointer ;
}
. tugtile__add-btn : hover {
background : var ( --background-modifier-hover ) ;
color : var ( --text-normal ) ;
}
/* ⋯ More actions */
. tugtile__tile-more {
color : var ( --text-faint ) ;
line-height : 1 ;
cursor : pointer ;
opacity : 0 . 3 ;
display : inline-flex ;
align-items : center ;
}
. tugtile__tile-more svg { width : 1 em ; height : 1 em ; }
. tugtile__tile-more : hover {
opacity : 1 ;
color : var ( --text-normal ) ;
}
/* Card checkbox */
. tugtile__check {
margin : 0 ;
flex : none ;
cursor : pointer ;
}
/* "Undo" button for toast notifications (to restore deleted/archived items in one tap) */
. tugtile-toast-undo {
margin-left : 10px ;
font-weight : 700 ;
text-decoration : underline ;
cursor : pointer ;
color : var ( --text-accent ) ;
}
/* Focused editing sheet (integrated with iOS 26 rounded sheet design; hides native ✕ to use our own left ❌ and right ✓) */
. tugtile-edit-modal-full {
width : 100vw ;
max-width : 100vw ;
2026-06-20 08:57:10 +00:00
height : 100dvh ; /* Dynamic viewport: shrinks with the on-screen keyboard so the editor stays inside the visible area and the textarea can scroll. dvh is supported on every Obsidian-supported renderer (Electron + iOS/Android webview), so no 100vh fallback is needed. */
2026-06-10 09:15:42 +00:00
max-height : 100dvh ;
border-radius : 0 ; /* Full-screen focus editor — nothing of the board shows behind */
/* Inset content past the iPad status bar / notch / home indicator; env() = 0 on desktop. The solid background still bleeds into these strips. */
padding : env ( safe-area-inset-top ) env ( safe-area-inset-right ) env ( safe-area-inset-bottom ) env ( safe-area-inset-left ) ;
overflow : hidden ;
transform-origin : center ;
animation : tugtile-pop-in 0 . 4s cubic-bezier ( . 34 , 1 . 56 , . 64 , 1 ) ; /* Keep the bounce — full-screen ≠ unrefined */
}
/* Backdrop darkens to solid FAST (so the board vanishes quickly, not lingering behind the bounce); zero container padding */
2026-06-20 08:57:10 +00:00
. modal-container . tugtile-edit-host { padding : 0 ; align-items : flex-start ; } /* Top-pinned, so shrinking the editor to the visual viewport (keyboard) keeps it above the keyboard. Class set in TileEditModal.onOpen — a direct container target, no parent-match selector. */
2026-06-10 09:15:42 +00:00
/ * The status-bar / home-indicator strips show --background-secondary ( white in light , grey in dark ) and sit
outside the modal ' s reach . So instead of fighting to cover them , dye the WHOLE editor --background-secondary
to match — seamless in both modes ( the old --background-primary only matched in light mode ) . * /
2026-06-20 08:57:10 +00:00
. modal-container . tugtile-edit-host . modal-bg { background : var ( - - background - secondary ) ; opacity : 1 ; animation : tugtile-bg-in 0.12 s ease both ; }
2026-06-10 09:15:42 +00:00
. tugtile-edit-modal-full ,
. tugtile-edit-modal-full . tugtile-edit-modal { background : var ( - - background - secondary ) ; }
/* macOS: push the editor down so its top bar (✕) isn't covered by the window traffic lights (class is .mod-macos) */
. mod-macos . tugtile-edit-modal-full { padding-top : 28 px ; }
@ keyframes tugtile-bg-in { from { opacity : 0 ; } to { opacity : 1 ; } }
. tugtile-edit-modal-full . tugtile-ed-closing {
animation : tugtile-pop-out 0 . 28s ease-in forwards ; /* Exit transition: reverse scaling animation */
}
/* Opacity reaches 1 early (~0.1s) so the editor covers the board fast — the scale still bounces to settle. Fixes the "laggy / see-through during the animation" feel. */
@ keyframes tugtile-pop-in {
0 % { opacity : 0 ; transform : scale ( 0.92 ) ; }
35 % { opacity : 1 ; }
100 % { opacity : 1 ; transform : scale ( 1 ) ; }
}
@ keyframes tugtile-pop-out {
from { opacity : 1 ; transform : scale ( 1 ) ; }
to { opacity : 0 ; transform : scale ( 0.92 ) ; }
}
2026-06-22 03:25:41 +00:00
/ * In-content text bars ( search · rename · add-lane ) — none of these use an Obsidian Modal any more . The view
renders a strip at the top of its content : . tugtile-searchbar is persistent ( toggled by . tugtile--searching ) ,
. tugtile-promptbar is transient ( built on demand for rename / add-lane ) . Both reuse the . tugtile-prompt-row /
-field / -input styles below , and the JS freezes the board height while they ' re open so the virtual keyboard
can ' t collapse it into a black gap . On phone they take over the . tugtile__ctlbar slot . * /
. tugtile-searchbar { display : none ; }
. tugtile--searching . tugtile-searchbar ,
. tugtile-promptbar {
flex : 0 0 auto ;
padding : 4px 8px 8px ;
background : var ( --background-primary ) ;
2026-06-10 09:15:42 +00:00
}
2026-06-22 03:25:41 +00:00
. tugtile--searching . tugtile-searchbar { display : block ; }
body . is-phone . tugtile--searching . tugtile__ctlbar ,
body . is-phone . tugtile--prompting . tugtile__ctlbar { display : none ; }
2026-06-10 09:15:42 +00:00
. tugtile-prompt-row { display : flex ; align-items : center ; gap : 8 px ; }
/* The input is wrapped in a rounded "field" so a leading icon (search) can sit inside it */
. tugtile-prompt-field {
flex : 1 ; min-width : 0 ;
display : flex ; align-items : center ; gap : 8px ;
box-sizing : border-box ; height : 40px ; padding : 0 12px ;
background : var ( --background-modifier-form-field ) ;
border : 1px solid var ( --background-modifier-border ) ;
border-radius : var ( --radius-m ) ;
overflow : hidden ; /* Clip the input to the rounded box so it can never poke out of the corners */
}
. tugtile-prompt-field : focus-within { border-color : var ( - - interactive - accent ) ; }
. tugtile-prompt-fieldicon { flex : 0 0 auto ; display : inline-flex ; align-items : center ; color : var ( - - text - muted ) ; }
. tugtile-prompt-fieldicon svg { width : 1.15 em ; height : 1.15 em ; }
2026-06-20 08:57:10 +00:00
/ * The input is our OWN contenteditable element ( not a native < input > ) , so Obsidian ' s input chrome never
applies — no overrides , no ! important . The . tugtile-prompt-field IS the visible box ; this is the bare
single-line text surface inside it . plaintext-only keeps paste / IME clean ( no rich markup , CJK-safe ) . * /
. tugtile-prompt-input {
flex : 1 ; min-width : 0 ;
outline : none ;
white-space : pre ; overflow-x : auto ; overflow-y : hidden ; /* single line: scroll sideways, never wrap */
font-size : 16px ; color : var ( --text-normal ) ; /* 16px prevents iOS auto-zoom on focus */
-webkit-user-modify : read-write-plaintext-only ;
}
. tugtile-prompt-input :: -webkit-scrollbar { display : none ; } /* hide the horizontal scrollbar on the one-line field */
. tugtile-prompt-input : empty :: before {
content : attr ( data-placeholder ) ;
color : var ( --text-faint ) ;
pointer-events : none ; /* placeholder text isn't selectable / doesn't block the caret */
2026-06-10 09:15:42 +00:00
}
/* .tugtile-prompt-x / -ok inherit from .tugtile-iconbtn (the ✓ colour override lives up there) */
. tugtile-edit-modal-full . modal-close-button { display : none ; } /* Hides the native top-right ✕ button in favor of our custom ❌ button with safety checks */
. tugtile-edit-modal {
display : flex ;
flex-direction : column ;
height : 100 % ;
min-height : 0 ;
padding : 0 ;
background : var ( --background-primary ) ; /* Opaque: in the marktile pane there's no modal-bg behind it, so without this the area the iOS keyboard reveals shows through as a black gap */
}
/ * marktile lives in a leaf , not a modal — fill the pane opaquely and use the dynamic viewport so the editor
shrinks WITH the on-screen keyboard instead of leaving a black void below it . * /
. workspace-leaf-content [ data-type = "marktile-editor" ] . view-content {
padding : 0 ;
background : var ( --background-primary ) ;
}
. workspace-leaf-content [ data-type = "marktile-editor" ] . tugtile-ed-scroll { max-height : 100 dvh ; }
. tugtile-ed-bar {
display : flex ;
align-items : center ;
gap : 6px ;
padding : 8px 10px ;
border-bottom : 1px solid var ( --background-modifier-border ) ;
}
/ * ─ ─ Single source of truth for EVERY icon button ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
editor ✕ / ✓ , toolbar tools , find / replace , search ✕ / ✓ . 40 × 40 , flat , non-flex centering . Two hard
rules baked in here so they can never drift again and re-break iPad :
1 ) NO display : flex on the button — iPad WebKit blanks an inline svg that ' s a flex item .
2 ) the svg is nested in a < span > ( done in JS ) — iPad WebKit won 't render an svg that' s a direct
button child . So a size rule on the svg is safe .
Per-button classes below only override what ' s genuinely unique ( colour of ✓ , hover colour of ✕ ) . * /
. tugtile-iconbtn {
flex : 0 0 auto ;
-webkit-appearance : none ; appearance : none ;
position : relative ;
width : 40px ; height : 40px ;
border : none ; border-radius : var ( --radius-m ) ;
background : transparent ; color : var ( --text-muted ) ;
cursor : pointer ;
}
/ * The icon ' s svg is wrapped in a < span > ( the iPad svg-in-button fix ) . Absolutely-centre that span — true
pixel-centre , and NO flex on the button ( flex on a < button > blanks the inline svg on iPad ) . * /
. tugtile-iconbtn > span { position : absolute ; top : 50 % ; left : 50 % ; transform : translate ( -50 % , -50 % ) ; display : block ; }
. tugtile-iconbtn svg { width : 20 px ; height : 20 px ; display : block ; }
. tugtile-iconbtn : hover { background : var ( - - background - modifier - hover ) ; color : var ( - - text - normal ) ; }
/* unique bits only */
. tugtile-ed-ok , . tugtile-ed-ok : hover { color : var ( - - color - green ) ; }
. tugtile-ed-ok { font-weight : 700 ; }
. tugtile-ed-x : hover { color : var ( - - text - error ) ; }
. tugtile-prompt-ok , . tugtile-prompt-ok : hover { color : var ( - - text - success , var ( - - interactive - accent ) ) ; }
. tugtile-ed-tools {
flex : 1 ; min-width : 0 ;
display : flex ; flex-wrap : nowrap ; gap : 4px ; align-items : center ;
justify-content : safe center ; /* Centered in the bar (not crammed next to ✕); falls back to start-aligned + scrollable when it overflows */
2026-06-20 08:57:10 +00:00
overflow-x : auto ;
2026-06-10 09:15:42 +00:00
}
/* .tugtile-ed-tool, .tugtile-ed-find-b, .tugtile-prompt-x/ok all inherit everything from .tugtile-iconbtn above */
/* Group divider in the editor toolbar */
. tugtile-ed-sep { flex : 0 0 auto ; width : 1 px ; height : 20 px ; margin : 0 4 px ; background : var ( - - background - modifier - border ) ; }
/* Phone: second toolbar row (format/insert tools), below the bar's essentials. Centered, scrolls if it overflows. */
. tugtile-ed-tools2 {
flex : 0 0 auto ;
display : flex ; flex-wrap : nowrap ; gap : 2px ; align-items : center ;
justify-content : safe center ;
2026-06-20 08:57:10 +00:00
overflow-x : auto ; -webkit-overflow-scrolling : touch ;
2026-06-10 09:15:42 +00:00
padding : 6px 10px ;
border-bottom : 1px solid var ( --background-modifier-border ) ;
}
2026-06-20 08:57:10 +00:00
/ * Thin horizontal scrollbar for the overflowing toolbar rows , styled via :: -webkit-scrollbar — Obsidian ' s
renderer is Chromium / WebKit on every platform , so this is the portable way to thin the bar ( the standard
thin-scrollbar property is only partially supported by Obsidian ' s older builds ) . * /
. tugtile-ed-tools :: -webkit-scrollbar ,
. tugtile-ed-tools2 :: -webkit-scrollbar { height : 6 px ; }
. tugtile-ed-tools :: -webkit-scrollbar-thumb ,
. tugtile-ed-tools2 :: -webkit-scrollbar-thumb { background : var ( - - background - modifier - border ) ; border-radius : 3 px ; }
2026-06-10 09:15:42 +00:00
body . is-phone . tugtile-ed-sep { display : none ; } /* no group separators in the compact phone rows */
/* Phone: pull the editor near the top so the toolbar hugs the Dynamic Island (4px breathing room below it) */
body . is-phone . tugtile-edit-modal-full { padding-top : 4 px ; }
body . is-phone . tugtile-ed-bar { padding-top : 6 px ; padding-bottom : 4 px ; }
/* Find / replace bar (below the toolbar) */
. tugtile-ed-find {
display : flex ; flex-wrap : wrap ; align-items : center ; gap : 6px ;
padding : 6px 10px ;
border-bottom : 1px solid var ( --background-modifier-border ) ;
background : var ( --background-secondary ) ;
}
. tugtile-ed-find-i {
flex : 1 ; min-width : 80px ;
font-size : 16px ; padding : 5px 8px ; border-radius : var ( --radius-s ) ; /* 16px avoids iOS focus-zoom */
}
. tugtile-ed-find-n { flex : 0 0 auto ; color : var ( - - text - muted ) ; font-size : 0.85 em ; min-width : 1.6 em ; text-align : center ; }
/* .tugtile-ed-find-b inherits everything from .tugtile-iconbtn */
/* Settings: compact checkbox grid for choosing which editor toolbar buttons show */
. tugtile-tools-pick { display : flex ; flex-wrap : wrap ; gap : 6 px 12 px ; justify-content : flex-end ; }
. tugtile-tools-pick . tugtile-tool-chk { display : inline-flex ; align-items : center ; gap : 4 px ; cursor : pointer ; }
. tugtile-tools-pick . tugtile-tool-chk input { margin : 0 ; }
. tugtile-tools-pick . tugtile-tool-chk-i { display : inline-flex ; align-items : center ; min-width : 1.2 em ; justify-content : center ; }
. tugtile-tools-pick . tugtile-tool-chk-i svg { width : 1.15 em ; height : 1.15 em ; }
. tugtile-ed-scroll { flex : 1 ; min-height : 0 ; overflow-y : auto ; -webkit- overflow-scrolling : touch ; }
/ * Single contenteditable surface — the visible styled text IS the editable text ( no overlay to keep aligned ) ,
which is what lets a heading line render bigger while the literal '## ' markers stay in place . * /
. tugtile-ed-rich {
min-height : 100 % ;
margin : 0 ; border : 0 ; outline : none ;
padding : 14px 16px ;
font-family : var ( --font-text ) ; font-size : 16px ; line-height : 1 . 7 ;
white-space : pre-wrap ; overflow-wrap : anywhere ; word-break : break-word ;
color : var ( --text-normal ) ; caret-color : var ( --text-normal ) ;
-webkit-user-modify : read-write-plaintext-only ; /* Keep paste/IME plaintext so no rich markup leaks into the markdown */
}
. tugtile-ed-rich : focus , . tugtile-ed-rich : focus-visible { outline : none ; box-shadow : none ; }
. tugtile-ed-rich . tg-line { min-height : 1.7 em ; } /* Empty lines stay tappable and visible */
/ * marktile IS a markdown editor , so its body font is monospace in BOTH modes ( Seasoned keeps the
syntax highlighting , just on a monospace base ) . Scoped to . marktile-ed so tugtile ' s card editor
( a board surface , not a markdown editor ) stays on the proportional --font-text . * /
. marktile-ed . tugtile-ed-rich { font-family : var ( - - font - monospace ) ; tab-size : 2 ; } /* default tab-size is 8 — far too wide; a tab shows as 2 cells (one space · rule · one space) */
/ * Distinguish a literal tab from spaces : a 1px accent rule drawn INSIDE each 2-cell tab ( one space · rule ·
one space ) so it neither hugs the right edge nor touches the following text . A background gradient ( not a
border ) lets the rule sit at an arbitrary x . marktile + Seasoned only — Plain ' s neutralizer strips backgrounds
too . padding-block extends the rule ' s height ( bg fills the padding box ) to ~ line-height so consecutive tab
lines join into one continuous vertical guide . Tune background-position ( rule x ) / padding-block ( continuity ) . * /
. marktile-ed . tg-tab {
background : linear-gradient ( var ( --text-accent ) , var ( --text-accent ) ) no-repeat ;
background-size : 1px 100 % ;
background-position : 1ch center ; /* one cell in; tab is 2 cells, so one cell of space after the rule */
padding-block : 0 . 35em ;
}
2026-06-13 12:55:53 +00:00
/ * A leading tab on a heading line must NOT inflate the indent guide — its 1ch position / 100 % height / 0 . 35em
padding are all font-size-relative , so on a grown heading the thin rule balloons . Counter-scale the tab ' s own
font-size by the heading ' s scale so its metrics fall back to the base text size ( the guide stays thin ) . * /
2026-06-13 13:10:35 +00:00
. marktile-ed . tg-h1 . tg-tab { font-size : calc ( 1 em / 1.7 ) ; padding-block : 0.85 em ; }
. marktile-ed . tg-h2 . tg-tab { font-size : calc ( 1 em / 1.45 ) ; padding-block : 0.68 em ; }
. marktile-ed . tg-h3 . tg-tab { font-size : calc ( 1 em / 1.25 ) ; padding-block : 0.55 em ; }
. marktile-ed . tg-h4 . tg-tab { font-size : calc ( 1 em / 1.12 ) ; padding-block : 0.45 em ; }
. marktile-ed . tg-h5 . tg-tab , . marktile-ed . tg-h6 . tg-tab { font-size : calc ( 1 em / 1.05 ) ; padding-block : 0.4 em ; }
2026-06-10 09:15:42 +00:00
/ * ─ ─ Table of contents ( marktile only ) . A toggle side panel of H1 – H3 ; click a heading to scroll to it .
Absolute overlay anchored ( top set in JS ) to the editor content , so the toolbar + the TOC toggle button
stay above / clickable . Desktop / iPad : a left sidebar that pushes the text right . Phone : a full-width overlay . * /
. marktile-ed { position : relative ; }
. marktile-ed . tugtile-ed-bar { position : relative ; z-index : 6 ; } /* keep the toggle (top-left) above the panel */
. marktile-toc {
position : absolute ; left : 0 ; bottom : 0 ; width : 240px ; z-index : 5 ;
background : var ( --background-secondary ) ; border-right : 1px solid var ( --background-modifier-border ) ;
overflow-y : auto ; padding : 0 . 6em 0 ;
transform : translateX ( -100 % ) ; transition : transform 0 . 18s ease ; /* slid out by default */
}
. marktile-toc-open . marktile-toc { transform : none ; }
. marktile-ed . tugtile-ed-scroll { transition : padding-left 0.18 s ease ; }
. marktile-toc-open . tugtile-ed-scroll { padding-left : 240 px ; } /* clear the sidebar (desktop/iPad) */
. marktile-toc-open . tugtile-ed-toc { color : var ( - - text - accent ) ; } /* toggle active state */
. marktile-toc-title { font-size : 0.8 em ; text-transform : uppercase ; letter-spacing : 0.06 em ; color : var ( - - text - muted ) ; padding : 0 1.1 em 0.5 em ; }
. marktile-toc-list { display : flex ; flex-direction : column ; }
. marktile-toc-item { padding : 0 1.1 em ; line-height : 40 px ; cursor : pointer ; white-space : nowrap ; overflow : hidden ; text-overflow : ellipsis ; } /* ~40px tap target (single-line, vertically centred) — button-like, easier to hit */
. marktile-toc-item : hover { background : var ( - - background - modifier - hover ) ; }
. marktile-toc-l1 { font-weight : 600 ; }
. marktile-toc-l2 { padding-left : 2.2 em ; }
. marktile-toc-l3 { padding-left : 3.3 em ; color : var ( - - text - muted ) ; font-size : 0.95 em ; }
. marktile-toc-empty { padding : 0.4 em 1.1 em ; color : var ( - - text - muted ) ; font-style : italic ; }
/* drag-reorder feedback (borrowed from tugtile's tile Sortable, scaled to a TOC row) */
. marktile-toc-item--ghost { opacity : 0.4 ; background : var ( - - background - modifier - hover ) ; } /* the gap left behind */
. marktile-toc-item--chosen { box-shadow : inset 0 0 0 2 px var ( - - interactive - accent ) ; } /* the picked row */
. marktile-toc-item--drag { /* the row following the finger (re-parented to <body> by fallbackOnBody) */
opacity : 0 . 97 ; background : var ( --background-secondary ) ; border-radius : 6px ;
box-shadow : 0 10px 24px rgba ( 0 , 0 , 0 , 0 . 4 ) ; cursor : grabbing ;
rotate : 3deg ; scale : 1 . 03 ; /* "picked up" tilt — standalone props (NOT transform) so Sortable's drag translation isn't overwritten */
}
. is-phone . marktile-toc { width : 100 % ; border-right : none ; } /* phone: full-width overlay (sidebar too cramped) */
. is-phone . marktile-toc-open . tugtile-ed-scroll { padding-left : 0 ; }
2026-06-22 03:25:41 +00:00
/ * marktile viewcycle → Plain mode neutralizer lives AFTER the syntax rules below — it must out-specify every one
of them WITHOUT ! important , so it ' s placed past the last competitor ( see the specificity-contract comment there ) . * /
2026-06-10 09:15:42 +00:00
/ * marktile lock → read-only . contenteditable = false alone is OVERRIDDEN on WebKit by the editor ' s
-webkit-user-modify : read-write-plaintext-only , so flip that to read-only here ( the real switch ) . Also
disable the toolbar so a stray tap can ' t edit ( Obsidian has no save step — whatever changes IS saved ) . * /
. tugtile--locked . tugtile-ed-rich { -webkit- user-modify : read-only ; user-modify : read-only ; }
. tugtile--locked . tugtile-ed-tools ,
. tugtile--locked . tugtile-ed-tools2 ,
. tugtile--locked . tugtile-ed-find { pointer-events : none ; opacity : 0.4 ; } /* find/replace too — else Replace All bypasses contenteditable (B2) */
/ * Phone : tall bottom padding gives scroll room so the last real line can always be scrolled up above the
on-screen keyboard ( see keepCaretVisible ) instead of being trapped under it . * /
body . is-phone . tugtile-ed-rich { padding-bottom : 55 vh ; }
/* Headings: markers kept (## still shows), the line just renders larger */
. tugtile-ed-rich . tg-h { font-weight : 700 ; color : var ( - - text - accent ) ; }
. tugtile-ed-rich . tg-h1 { font-size : 1.7 em ; line-height : 1.35 ; }
. tugtile-ed-rich . tg-h2 { font-size : 1.45 em ; line-height : 1.4 ; }
. tugtile-ed-rich . tg-h3 { font-size : 1.25 em ; }
. tugtile-ed-rich . tg-h4 { font-size : 1.12 em ; }
. tugtile-ed-rich . tg-h5 , . tugtile-ed-rich . tg-h6 { font-size : 1.05 em ; }
. tugtile-ed-rich . tg-quote { color : var ( - - text - muted ) ; }
. tugtile-ed-rich . tg-b { font-weight : 700 ; }
. tugtile-ed-rich . tg-i { font-style : italic ; }
. tugtile-ed-rich . tg-strike { text-decoration : line-through ; color : var ( - - text - muted ) ; }
. tugtile-ed-rich . tg-check { font-family : var ( - - font - monospace ) ; color : var ( - - text - accent ) ; }
. tugtile-ed-rich . tg-check-done { color : var ( - - color - green ) ; }
. tugtile-ed-rich . tg-task-done { color : var ( - - text - muted ) ; } /* whole completed task line dims */
. tugtile-ed-rich . tg-code { font-family : var ( - - font - monospace ) ; color : var ( - - text - accent ) ; }
. tugtile-ed-rich . tg-tag ,
. tugtile-ed-rich . tg-link { color : var ( - - text - accent ) ; }
. tugtile-ed-rich . tg-date { color : var ( - - color - yellow ) ; }
2026-06-13 12:10:12 +00:00
/ * Seasoned "彩色" palette — opt-in via settings ( seasonedColor → . marktile-palette-color on the editor mount ) .
Each syntax token gets its own hue instead of the single accent tint . Obsidian theme color vars ( hex fallbacks
for the web host ) so it tracks light / dark + custom themes . quote / strike / date / check keep their base styling . * /
2026-06-13 12:55:53 +00:00
. marktile-palette-color . tugtile-ed-rich . tg-h { color : var ( - - color - orange , #d19a66 ) ; }
. marktile-palette-color . tugtile-ed-rich . tg-b { color : var ( - - color - yellow , #e5c07b ) ; }
2026-06-13 12:10:12 +00:00
. marktile-palette-color . tugtile-ed-rich . tg-i { color : var ( - - color - purple , #c678dd ) ; }
. marktile-palette-color . tugtile-ed-rich . tg-code { color : var ( - - color - green , #98c379 ) ; }
. marktile-palette-color . tugtile-ed-rich . tg-tag ,
2026-06-13 12:55:53 +00:00
. marktile-palette-color . tugtile-ed-rich . tg-link { color : var ( - - color - cyan , #56b6c2 ) ; }
2026-06-22 03:25:41 +00:00
/ * Plain mode = a hard reset of ALL the syntax styling above to a bare monospace baseline . This used to be one
! important block ; it ' s now specificity-only ( no ! important ) so styles . css is 100 % ! important-free .
⚠ ️ SPECIFICITY CONTRACT — do not weaken , and keep this block AFTER every syntax rule above :
• every syntax rule above tops out at ( 0 , 3 , 0 ) — the . marktile-palette-color and . marktile-ed . tg-hN . tg-tab rules .
• ` . tugtile-plain . tugtile-ed-rich . tg-line [ * ] ` = ( 0 , 3 , 0 ) : covers non-marktile hosts ( tugtile card
editor , web ) , where NO ( 0 , 3 , 0 ) competitor exists ( palette + tab rules are all . marktile- * gated → only ( 0 , 2 , 0 ) ) .
• ` . marktile-ed . tugtile-plain . tugtile-ed-rich . tg-line [ * ] ` = ( 0 , 4 , 0 ) : clears marktile ' s ( 0 , 3 , 0 ) palette + tab rules .
. tugtile-plain sits on the editor mount ( an ANCESTOR of . tugtile-ed-rich ) ; . tg-line are direct children of it .
If you ever add a syntax rule at ( 0 , 4 , 0 ) + , or a new editor host that runs Plain WITHOUT . marktile-ed , Plain mode
silently stops neutralising ( symptom : colours / sizes leak through ) — fix it here . There is no test guarding this . * /
. tugtile-plain . tugtile-ed-rich . tg-line ,
. tugtile-plain . tugtile-ed-rich . tg-line * ,
. marktile-ed . tugtile-plain . tugtile-ed-rich . tg-line ,
. marktile-ed . tugtile-plain . tugtile-ed-rich . tg-line * {
font-size : 1em ; font-weight : normal ; font-style : normal ;
text-decoration : none ; color : var ( --text-normal ) ; background : transparent ;
font-family : var ( --font-monospace ) ; line-height : inherit ; /* monospace = raw-source feel */
border : 0 ; padding-left : 0 ; margin : 0 ;
2026-06-10 09:15:42 +00:00
}
/* Lane header container */
. tugtile__lane-title {
flex : 1 ;
cursor : text ;
white-space : nowrap ; /* Prevents the title from wrapping to expand the lane width, avoiding overlap with count/buttons */
overflow : hidden ;
text-overflow : ellipsis ;
transform-origin : left center ; /* Tucks toward the chevron (left) on collapse */
transition : transform var ( --tg-d1 ) ease , opacity var ( --tg-d1 ) ease ;
}
. tugtile__lane-title-v { display : none ; } /* Vertical title clone — only rendered in the collapsed strip */
/* Collapse stage 1 / expand stage 2: lane is narrow, the horizontal title is tucked into the chevron, and the row trimmings fade */
. tugtile__lane--narrowing { width : 40 px ; }
. tugtile__lane--narrowing . tugtile__lane-title { transform : scaleX ( 0 ) ; opacity : 0 ; }
. tugtile__lane--narrowing . tugtile__lane-count ,
. tugtile__lane--narrowing . tugtile__lane-more { opacity : 0 ; }
/* Responsive board (opt-in, default off): on a narrow pane the horizontal lanes reflow into a single full-width vertical stack — the behaviour of the retired List view, now automatic via a container query. Drag-and-drop/editing logic is shared with the board view. */
. tugtile--rwd { container-type : inline - size ; container-name : tugtileboard ; }
@ container tugtileboard ( max-width : 640px ) {
. tugtile__board { flex-direction : column ; overflow-x : hidden ; overflow-y : auto ; align-items : stretch ; }
. tugtile__lane { width : auto ; max-width : none ; flex : 0 0 auto ; }
. tugtile__addcol { width : auto ; }
/* Collapsed lane in vertical layout: keep a standard horizontal header (the board view's narrow vertical strip would become a full-width black box here). The board view's two-stage strip animation no-ops here — the horizontal title stays, the vertical clone is unused. */
. tugtile__lane--collapsed { min-width : 0 ; max-width : none ; width : auto ; }
. tugtile__lane--collapsed . tugtile__lane-head { flex-direction : row ; height : auto ; padding : 8 px ; overflow : visible ; }
. tugtile__lane--collapsed . tugtile__lane-title { display : revert ; writing-mode : horizontal - tb ; max-height : none ; transform : none ; opacity : 1 ; }
. tugtile__lane--collapsed . tugtile__lane-title-v { display : none ; }
. tugtile__lane--narrowing { width : auto ; }
. tugtile__lane--narrowing . tugtile__lane-title { transform : none ; opacity : 1 ; }
}
/* Centered header view-cycle control: "<icon> <name>" — click cycles board → table → markdown. Same padding as the lock button so the "·" separator sits symmetrically between them. */
. tugtile-viewcycle {
display : inline-flex ; align-items : center ; gap : 5px ;
cursor : pointer ; border-radius : var ( --radius-s ) ;
padding : 2px 6px ;
}
. tugtile-viewcycle : hover { background : var ( - - background - modifier - hover ) ; }
. tugtile-viewcycle-icon { display : inline-flex ; }
. tugtile-viewcycle-icon svg { width : 1 em ; height : 1 em ; }
. tugtile-viewcycle-name { font-weight : var ( - - font - medium , 500 ) ; }
/* Table view */
. tugtile__table-wrap {
height : 100 % ;
overflow : auto ;
padding : 0 12px ; /* No top padding → the sticky header sits flush at the top, so rows can't peek above it while scrolling */
}
. tugtile__table {
width : 100 % ;
border-collapse : collapse ;
}
. tugtile__table th ,
. tugtile__table td {
text-align : left ;
padding : var ( --tugtile-row-pad , 8px ) 10px ; /* Vertical padding is the table density preset (settings) */
border-bottom : 1px solid var ( --background-modifier-border ) ;
vertical-align : top ;
}
. tugtile__table th {
cursor : pointer ;
position : sticky ;
top : 0 ;
z-index : 2 ; /* Stay above scrolling rows */
background : var ( --background-primary ) ; /* Solid — rows scroll cleanly behind, no show-through */
box-shadow : 0 1px 0 var ( --background-modifier-border ) ;
user-select : none ;
}
. tugtile__table th : hover { color : var ( - - text - accent ) ; }
. tugtile__row : hover { background : var ( - - background - modifier - hover ) ; }
. tugtile__row-title { cursor : text ; }
/* Table rows: displays plain text where we manage styles (without markdown rendering), keeping font sizes uniform and truncating long text to avoid breaking the layout */
. tugtile__row-text { font-size : var ( - - font - text - size ) ; white-space : nowrap ; overflow : hidden ; text-overflow : ellipsis ; max-width : 60 ch ; display : inline-block ; vertical-align : bottom ; }
. tugtile__row-title input { margin-right : 6 px ; }
. tugtile__row-lane { color : var ( - - text - muted ) ; white-space : nowrap ; }
/* Tag cell stays a normal table cell (NOT display:flex — that breaks cell layout and collapses when empty). Chips flow inline-block instead. */
. tugtile__row-tags . tugtile__tag { display : inline-block ; margin : 0 4 px 2 px 0 ; }
/* Drag-to-archive zone (only shown when dragging cards) */
. tugtile__archivebar {
display : none ;
align-items : center ;
justify-content : center ;
gap : 8px ;
margin : 0 12px 8px ;
padding : 12px ;
border : 2px dashed var ( --background-modifier-border ) ;
border-radius : var ( --radius-m ) ;
background : var ( --background-secondary ) ;
color : var ( --text-muted ) ;
font-weight : 600 ;
}
/* Lane collapse chevron: a visible rounded square button. Its left edge lines up with the tile (card) left edge below — both sit at the lane's inner padding (8px), so no extra offset is needed. Square size also gives a comfortable tap target. */
. tugtile__lane-chevron {
display : inline-flex ;
align-items : center ;
justify-content : center ;
flex : 0 0 auto ;
width : 1 . 6em ;
height : 1 . 6em ;
border-radius : var ( --radius-s ) ;
/ * Opaque ( translucent hover tint composited over the lane bg ) so the vertical title retracting INTO the
chevron during collapse / expand is fully masked — a see-through button would leak that text . * /
background : linear-gradient ( var ( --background-modifier-hover ) , var ( --background-modifier-hover ) ) , var ( --background-secondary ) ;
cursor : pointer ;
color : var ( --text-muted ) ;
user-select : none ;
position : relative ;
z-index : 3 ; /* Highest in the lane header, so it sits over the retracting title */
}
. tugtile__lane-chevron : hover { background : linear-gradient ( var ( - - background - modifier - border ) , var ( - - background - modifier - border ) ) , var ( - - background - secondary ) ; color : var ( - - text - normal ) ; }
/* Only the inner glyph rotates (the button stays still). Rotating the same ▾ by -90° to ▸ rather than swapping glyphs keeps height perfectly consistent. */
. tugtile__lane-chevron-glyph {
display : inline-flex ;
transition : transform 0 . 3s cubic-bezier ( . 34 , 1 . 56 , . 64 , 1 ) ; /* Springy overshoot */
}
. tugtile__lane-chevron-glyph svg { width : 1.1 em ; height : 1.1 em ; }
. tugtile__lane--collapsed . tugtile__lane-chevron-glyph { transform : rotate ( -90 deg ) ; }
/ * In the collapsed vertical strip : drop the margin AND pin the toggle to the top-left corner ( align-self :
flex-start ) instead of centering it — so it stays exactly where the expanded header ' s chevron was and
doesn ' t drift sideways when a lane collapses / expands . * /
. tugtile__lane--collapsed . tugtile__lane-chevron { margin : 0 ; align-self : flex-start ; }
/* Collapsed lanes: a fixed narrow column with vertical title orientation (upright CJK characters, truncated if too long) */
. tugtile__lane--collapsed {
width : 40px ; /* Base lane sets a plain width + width transition, so collapsing to 40px animates smoothly */
max-height : 100 % ;
cursor : pointer ;
}
. tugtile__lane--collapsed . tugtile__list ,
. tugtile__lane--collapsed . tugtile__add ,
. tugtile__lane--collapsed . tugtile__lane-more {
display : none ;
}
. tugtile__lane--collapsed . tugtile__lane-head {
flex-direction : column ;
align-items : center ;
justify-content : flex-start ; /* Reset the base header's space-between, which in a column would spread chevron/title/count apart and push the chevron off the top */
gap : 10px ;
padding : 4px 0 8px ; /* Top 4 matches the expanded header so the chevron doesn't jump down when a lane collapses */
height : 100 % ;
overflow : hidden ;
}
. tugtile__lane--collapsed . tugtile__lane-title { display : none ; } /* Horizontal title hidden in the strip; the vertical clone takes over */
. tugtile__lane--collapsed . tugtile__lane-title-v {
display : block ;
writing-mode : vertical-rl ;
text-orientation : mixed ; /* Keeps CJK characters upright while rotating alphanumeric text */
white-space : nowrap ; /* Prevents wrapping into multiple vertical lines, which is the primary cause of text overlap */
overflow : hidden ;
text-overflow : ellipsis ;
max-height : 100 % ;
flex : 0 1 auto ;
cursor : pointer ;
transform : scaleY ( 0 ) ; /* Grows down out of / shrinks up into the chevron */
opacity : 0 ;
transform-origin : top center ;
/ * Grow and retract are the SAME motion in reverse : scaleY + opacity , one shared easing + duration , both
directions — so "grow out" and "shrink in" read as one symmetric concept ( a dissolve into the button ) . * /
transition : transform var ( --tg-d2 ) ease-in-out , opacity var ( --tg-d2 ) ease-in-out ;
}
. tugtile__lane--collapsed . tugtile__lane--grown . tugtile__lane-title-v { transform : scaleY ( 1 ) ; opacity : 1 ; }
/ * Retract = the exact reverse of grow ( same easing + duration ) , driven by a keyframe so it reliably animates
instead of snapping . scaleY + opacity dissolve up into the chevron in lockstep . * /
. tugtile__lane--retracting . tugtile__lane-title-v { animation : tugtile-title-retract var ( - - tg - d2 ) ease-in-out both ; }
@ keyframes tugtile-title-retract {
from { transform : scaleY ( 1 ) ; opacity : 1 ; }
to { transform : scaleY ( 0 ) ; opacity : 0 ; }
}
. tugtile__lane--collapsed . tugtile__lane-count {
writing-mode : horizontal-tb ;
flex : 0 0 auto ;
margin-top : auto ; /* Sink the count to the bottom of the strip (chevron + title stay grouped at the top) */
}
/* Lane ⋯ menu */
. tugtile__lane-more {
cursor : pointer ;
color : var ( --text-faint ) ;
opacity : 0 . 4 ;
display : inline-flex ;
align-items : center ;
}
. tugtile__lane-more svg { width : 1 em ; height : 1 em ; }
. tugtile__lane-more : hover {
opacity : 1 ;
color : var ( --text-normal ) ;
}
/* Archive list modal */
. tugtile-archive-list {
display : flex ;
flex-direction : column ;
gap : 8px ;
max-height : 60vh ;
overflow-y : auto ;
}
. tugtile-archive-row {
display : flex ;
align-items : center ;
gap : 8px ;
padding : 8px ;
border : 1px solid var ( --background-modifier-border ) ;
border-radius : var ( --radius-m ) ;
}
. tugtile-archive-body {
flex : 1 ;
min-width : 0 ;
}
. tugtile-archive-body > : first-child { margin-top : 0 ; }
. tugtile-archive-body > : last-child { margin-bottom : 0 ; }
. tugtile-archive-actions {
flex : 0 0 auto ;
display : flex ;
gap : 6px ;
}
/* Add lane container */
. tugtile__addcol {
flex : 0 0 auto ;
width : var ( --tugtile-lane-width , 300px ) ; /* Same fixed width as a lane, so the add-lane button lines up with the columns */
/* No padding: the button's top lines up with the lane boxes (both at the board padding), and the board's 12px gap already spaces it from the last lane */
}
. tugtile__addcol-btn {
width : 100 % ;
text-align : left ; /* Match the add-card button (consistent left-aligned "+ label") */
background : transparent ;
border : 1px dashed var ( --background-modifier-border ) ;
border-radius : var ( --radius-m ) ;
color : var ( --text-muted ) ;
padding : 8px ;
cursor : pointer ;
}
. tugtile__addcol-btn : hover {
background : var ( --background-modifier-hover ) ;
color : var ( --text-normal ) ;
}
2026-06-12 07:23:09 +00:00
/ * ─ ─ In-grid markdown tables — the "locked markers" editor ( decorateTables in editor-core . js ) . SHARED by
marktile ( inlined ) and ejecta ( this file is vendored as tile-core . css ) . Gate = . marktile-grid : hosts add it
in Seasoned + Rendered , drop it in Plain . The pipe ` | ` is the table ' s ` # # ` — in Seasoned it stays VISIBLE
( dimmed , locked ) so the markdown coexists with the layout ; the . tugtile-preview overlay ( Rendered ) hides the
pipes into a real bordered grid . textContent is byte-identical either way → round-trip exact . ( Dormant in
tugtile , which never sets . marktile-grid . ) * /
. marktile-grid . ej-trow { display : table-row ; white-space : normal ; }
. marktile-grid . ej-trow . ej-cell { display : table-cell ; padding : 3 px 12 px ; vertical-align : top ; cursor : text ; min-width : 36 px ; }
. marktile-grid . ej-trow . ej-pipe { display : table-cell ; color : var ( - - text - faint , #9aa0a6 ) ; padding : 0 1 px ; } /* the locked pipe, dimmed — the visible "seasoning" in Seasoned */
. marktile-grid . ej-tsep . ej-cell { color : var ( - - text - faint , #9aa0a6 ) ; } /* the |---| separator row, dimmed (also shown in Seasoned) */
. marktile-grid . ej-trow . ej-cell [ data-a = center ] { text-align : center ; }
. marktile-grid . ej-trow . ej-cell [ data-a = right ] { text-align : right ; }
/* Rendered overlay: the markers give way to a real grid (pipes + separator gone, cells get borders). */
. tugtile-preview . ej-trow . ej-pipe { display : none ; }
. tugtile-preview . ej-tsep { display : none ; }
. tugtile-preview . ej-trow . ej-cell { border : 1 px solid var ( - - background - modifier - border , #e4e6e9 ) ; padding : 6 px 14 px ; }
. tugtile-preview . ej-trow . ej-thead . ej-cell { background : var ( - - background - secondary , #f6f7f9 ) ; font-weight : 700 ; }
/ * Rendered hides EVERY marker , not just the table pipes — the ` # # ` / ` * * ` / ` ` ` ` ` markers ( . tg-mk ) give way too , so
the whole view reads as output ( the third point of raw → coexist → output ) . Still fully editable ; markup never
leaves the text . Dormant in tugtile ( no . tugtile-preview ) . * /
. tugtile-preview . tg-mk { display : none ; }
/* table right-click menu (desktop: insert/delete row/column) */
. ej-tblmenu { position : fixed ; z-index : 2000 ; min-width : 168 px ; background : var ( - - background - primary , #fff ) ; border : 1 px solid var ( - - background - modifier - border , #e4e6e9 ) ; border-radius : 10 px ; box-shadow : 0 10 px 30 px rgba ( 10 , 22 , 40 , .18 ) ; padding : 5 px ; display : flex ; flex-direction : column ; }
. ej-tblmenu button { font : 13.5 px var ( - - font - interface , system - ui , sans-serif ) ; text-align : left ; border : 0 ; background : transparent ; border-radius : 7 px ; padding : 8 px 12 px ; cursor : pointer ; color : var ( - - text - normal , #1f2328 ) ; }
. ej-tblmenu button : hover : not ( : disabled ) { background : var ( - - background - modifier - hover , rgba ( 0 , 0 , 0 , .06 ) ) ; }
. ej-tblmenu button : disabled { opacity : .35 ; cursor : default ; }
. ej-tblmenu hr { border : 0 ; border-top : 1 px solid var ( - - background - modifier - border , #e4e6e9 ) ; margin : 4 px 6 px ; }
/ * Inline image thumbnails ( decorateImages in editor-core . js ) — the picture shows beside its still-editable source
line ; the source markdown stays but dims . Hidden in Plain so power users see raw source only . Shared by marktile
+ ejecta ; dormant in tugtile ( never calls decorateImages ) . The first step of Rendered "growing" . * /
. ej-inlimg { display : block ; max-width : min ( 280 px , 100 % ) ; max-height : 220 px ; border-radius : 8 px ; margin : 4 px 0 8 px ; border : 1 px solid var ( - - background - modifier - border , #e4e6e9 ) ; }
. tg-line . ej-hasimg { color : var ( - - text - faint , #9aa0a6 ) ; font-size : 12 px ; }
. tugtile-plain . ej-inlimg { display : none ; }
. tugtile-plain . tg-line . ej-hasimg { color : var ( - - text - normal , #1f2328 ) ; font-size : 1 em ; }