mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-25 00:47:39 +00:00
feat(theme): polish Lines & Zen; soft gradient page backdrop
Lines theme: - Row hover tint (light + dark), wrapped in @media (hover: hover) so it doesn't stick on touch, and gated to :not(.isSelected) - Drop placeholder: 2px accent top line at 30% ghost opacity - Subtasks get separator lines between them (not at block boundaries) - Boards: hairline separators between cards in board columns - Worklog year/month dividers: dashed -> solid - Issue panel suggestions: hairline-separated list - Suppress inherited dashed accent border on current task; faint accent tint for planner/board current task - :focus-visible outline on task/planner-task Zen + Lines: - Banner: neutral elevated bg (6% black light, 5% white dark) so it reads as distinct without re-introducing card chrome Page backdrop (page.scss body:before): - Replace flat primary-color tint with a soft primary-tinted gradient - Light: two glows in different primary shades (primary-200 top-left, primary-400 bottom-right) for tonal variation on white - Dark: single primary corner glow + faint linear wash - Themes that hide body:before (dark-base, arc, nord-snow-storm, dracula, velvet) keep their own backdrops
This commit is contained in:
parent
b72c24eeff
commit
08d195a15d
3 changed files with 170 additions and 34 deletions
|
|
@ -16,10 +16,15 @@ body task:last-of-type {
|
|||
border-bottom: 1px solid var(--separator-color);
|
||||
}
|
||||
|
||||
/* No separators on sub-tasks — they form a connected indented block under the parent */
|
||||
body .sub-tasks task,
|
||||
body .sub-tasks task:last-of-type {
|
||||
/* Subtasks get separator lines between them.
|
||||
Suppress only the boundaries so the indented block reads as part of its parent:
|
||||
no top line on the first subtask (header → block flows together), no bottom line
|
||||
on the last subtask (parent's bottom border or next parent's top closes it). */
|
||||
body .sub-tasks task:first-of-type {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
body .sub-tasks task:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
|
|
@ -44,16 +49,19 @@ body task.isSelected .sub-tasks .box {
|
|||
background: transparent !important;
|
||||
}
|
||||
|
||||
/* === Planner: tight list with horizontal separator lines === */
|
||||
/* === Planner & Boards: tight list with horizontal separator lines === */
|
||||
|
||||
/* Remove the per-row gap so lines act as the only separator */
|
||||
/* Remove the per-row gap so lines act as the only separator.
|
||||
`.task-items` is the board column list — same treatment. */
|
||||
body .normal-tasks-items > *,
|
||||
body .deadline-items > *,
|
||||
body .scheduled-items > * {
|
||||
body .scheduled-items > *,
|
||||
body .task-items > planner-task {
|
||||
margin-bottom: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
/* Strip box-style chrome from every planner item host */
|
||||
/* Strip box-style chrome from every planner/board item host */
|
||||
body planner-task,
|
||||
body planner-deadline-task,
|
||||
body planner-repeat-projection,
|
||||
|
|
@ -71,14 +79,16 @@ body planner-repeat-projection > div {
|
|||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
/* Top separator on every direct child of the normal/deadline lists */
|
||||
/* Top separator on every direct child of the normal/deadline/board lists */
|
||||
body .normal-tasks-items > *,
|
||||
body .deadline-items > * {
|
||||
body .deadline-items > *,
|
||||
body .task-items > planner-task {
|
||||
border-top: 1px solid var(--separator-color) !important;
|
||||
}
|
||||
|
||||
body .normal-tasks-items > *:last-child,
|
||||
body .deadline-items > *:last-child {
|
||||
body .deadline-items > *:last-child,
|
||||
body .task-items > planner-task:last-child {
|
||||
border-bottom: 1px solid var(--separator-color) !important;
|
||||
}
|
||||
|
||||
|
|
@ -91,3 +101,102 @@ body .scheduled-items > .scheduled-item {
|
|||
body .scheduled-items > .scheduled-item:last-child {
|
||||
border-bottom: 1px solid var(--separator-color);
|
||||
}
|
||||
|
||||
/* === Row hover: subtle tint so the list feels responsive === */
|
||||
|
||||
/* Wrap in @media (hover: hover) — on touch, :hover sticks on the last-tapped row. */
|
||||
@media (hover: hover) {
|
||||
/* Use :has() so hovering a subtask doesn't also highlight its parent row.
|
||||
The .box is the absolute-positioned background layer of a task. */
|
||||
body task:hover:not(:has(task:hover)) > swipe-block > .inner-wrapper > .box {
|
||||
background: rgba(0, 0, 0, 0.04) !important;
|
||||
}
|
||||
|
||||
body.isDarkTheme
|
||||
task:hover:not(:has(task:hover))
|
||||
> swipe-block
|
||||
> .inner-wrapper
|
||||
> .box {
|
||||
background: rgba(255, 255, 255, 0.03) !important;
|
||||
}
|
||||
|
||||
body planner-task:hover {
|
||||
background: rgba(0, 0, 0, 0.04) !important;
|
||||
}
|
||||
|
||||
body.isDarkTheme planner-task:hover {
|
||||
background: rgba(255, 255, 255, 0.03) !important;
|
||||
}
|
||||
|
||||
/* Selection wins over hover */
|
||||
body task.isSelected:hover > swipe-block > .inner-wrapper > .box {
|
||||
background: var(--c-dark-10) !important;
|
||||
}
|
||||
|
||||
body.isDarkTheme task.isSelected:hover > swipe-block > .inner-wrapper > .box {
|
||||
background: var(--c-light-05) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === Current task: suppress inherited border === */
|
||||
|
||||
/* The base theme paints a dashed accent border around the current task's box —
|
||||
too noisy for a hairline-only list. The task component already renders a
|
||||
progress bar below the title, which is enough indication. */
|
||||
body task.isCurrent .box,
|
||||
body task.isCurrent.isCurrent.isCurrent.isCurrent.isCurrent .box {
|
||||
border-color: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* Planner/board tasks don't render a progress bar — without an indicator the
|
||||
running task disappears. Use a faint accent tint instead of a border. */
|
||||
body planner-task.isCurrent {
|
||||
border: none !important;
|
||||
background: color-mix(in srgb, var(--c-accent) 10%, transparent) !important;
|
||||
}
|
||||
|
||||
/* === Drop indicator: ghost slot with an accent top line === */
|
||||
|
||||
/* Default opacity is 0.1 — too faint between hairlines. Bump to 0.3 so the
|
||||
ghost is visible and add a 2px accent top line marking the drop position. */
|
||||
body task.cdk-drag-placeholder,
|
||||
body planner-task.cdk-drag-placeholder {
|
||||
opacity: 0.3 !important;
|
||||
border-top: 2px solid var(--c-accent) !important;
|
||||
}
|
||||
|
||||
/* === Keyboard focus: explicit outline so focus is never invisible === */
|
||||
|
||||
body task:focus-visible {
|
||||
outline: 2px solid var(--palette-primary-400) !important;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
body planner-task:focus-visible {
|
||||
outline: 2px solid var(--palette-primary-400) !important;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
/* === Worklog: switch dashed dividers to solid for visual consistency === */
|
||||
body worklog .year,
|
||||
body worklog .month {
|
||||
border-top-style: solid;
|
||||
}
|
||||
|
||||
/* === Issue panel suggestions: hairline-separated list === */
|
||||
|
||||
body issue-provider-tab .item-list {
|
||||
gap: 0 !important;
|
||||
}
|
||||
|
||||
body issue-preview-item {
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
border-radius: 0 !important;
|
||||
border-top: 1px solid var(--separator-color);
|
||||
}
|
||||
|
||||
body issue-preview-item:last-of-type {
|
||||
border-bottom: 1px solid var(--separator-color);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -255,3 +255,14 @@ body .week-month-btn:not(.active) {
|
|||
app-root.isWorkViewScrolled task.isCurrent .box {
|
||||
background: var(--bg) !important;
|
||||
}
|
||||
|
||||
/* Banner: zen normally hides chrome; give it a neutral elevated bg so it
|
||||
reads as distinct from the page without re-introducing primary color. */
|
||||
body banner .content-wrapper {
|
||||
background: rgba(0, 0, 0, 0.06) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
body.isDarkTheme banner .content-wrapper {
|
||||
background: rgba(255, 255, 255, 0.05) !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
@use 'angular-material-css-vars' as mat-css-vars;
|
||||
@use '../_common';
|
||||
@use './mixins/media-queries';
|
||||
|
||||
|
|
@ -139,16 +138,17 @@ body {
|
|||
opacity: 0;
|
||||
}
|
||||
|
||||
&.isLightTheme {
|
||||
// Soft primary-tinted gradient backdrop. Light and dark want different
|
||||
// recipes — the same single corner-glow that reads as warm on dark looks
|
||||
// smudgy on white, so light gets two larger diffuse glows (primary +
|
||||
// accent) for subtle depth without flat-tint feel.
|
||||
// Themes that want a different/no backdrop hide body:before (dark-base,
|
||||
// arc, nord-snow-storm, dracula, velvet do this).
|
||||
&.isLightTheme,
|
||||
&.isDarkTheme {
|
||||
color: var(--text-color);
|
||||
background: var(--bg);
|
||||
|
||||
&:before {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
filter: saturate(0.8);
|
||||
background: mat-css-vars.mat-css-color(50, 1);
|
||||
}
|
||||
&.isDisableBackgroundTint {
|
||||
background: var(--bg);
|
||||
&:before {
|
||||
|
|
@ -158,23 +158,39 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
&.isDarkTheme {
|
||||
color: var(--text-color);
|
||||
background: var(--bg);
|
||||
&.isLightTheme:before {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
// Two glows in different primary shades — primary-200 (soft pastel)
|
||||
// for the top-left highlight, primary-400 (deeper) for bottom-right
|
||||
// depth. Single-hue keeps the light-mode wash calm.
|
||||
background:
|
||||
radial-gradient(
|
||||
ellipse 120% 80% at 0% -10%,
|
||||
color-mix(in srgb, var(--palette-primary-200) 40%, transparent) 0%,
|
||||
transparent 65%
|
||||
),
|
||||
radial-gradient(
|
||||
ellipse 80% 60% at 100% 100%,
|
||||
color-mix(in srgb, var(--palette-primary-400) 15%, transparent) 0%,
|
||||
transparent 60%
|
||||
);
|
||||
}
|
||||
|
||||
&:before {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
filter: brightness(0.1);
|
||||
background: mat-css-vars.mat-css-color(900, 1);
|
||||
}
|
||||
&.isDisableBackgroundTint {
|
||||
background: var(--bg);
|
||||
&:before {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
&.isDarkTheme:before {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
background:
|
||||
radial-gradient(
|
||||
ellipse 80% 60% at 10% -10%,
|
||||
color-mix(in srgb, var(--c-primary) 10%, transparent) 0%,
|
||||
transparent 55%
|
||||
),
|
||||
linear-gradient(
|
||||
180deg,
|
||||
color-mix(in srgb, var(--c-primary) 4%, transparent) 0%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
// also hide material dialogs while import is in progress
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue