diff --git a/themes/Theme-Velour/README.md b/themes/Theme-Velour/README.md new file mode 100644 index 00000000..d58c1ccb --- /dev/null +++ b/themes/Theme-Velour/README.md @@ -0,0 +1,34 @@ +# Velour + +A velvet-lounge look for Stash v0.31+. Deep plum that reads as black until lit, +dusty rose for actions, champagne for small flourishes, and italic Instrument +Serif titles over Outfit. + +- Cards are framed plum tiles: the cover sits inset with rounded corners and the + text runs below it, lifting with a soft rose glow on hover. +- Controls are 10px rounded rectangles; main actions are rose pills. +- Covers the list toolbar, filter sidebar, selection toolbar, pagination, list, + wall and tagger views, scene and detail pages, stats and settings, at desktop + and phone widths. +- Fonts are bundled in `assets/fonts` (SIL OFL 1.1), so the theme makes no + external requests. + +Pure CSS; no change to Stash behaviour. Enable one theme at a time. + +## Screenshots + +Taken on Stash v0.31.1 with demo content: fictional titles and names, blurred openly licensed photos (credits on the [screenshots branch](https://github.com/nexalapp/CommunityScripts/tree/screenshots)). + +| Scenes | Scene | +|---|---| +| ![Scenes](https://raw.githubusercontent.com/nexalapp/CommunityScripts/b03f96d614bb72c4df7a72e5227bcd8f2e04833f/Theme-Velour/desktop-scenes.jpg) | ![Scene](https://raw.githubusercontent.com/nexalapp/CommunityScripts/b03f96d614bb72c4df7a72e5227bcd8f2e04833f/Theme-Velour/desktop-scene.jpg) | + +| Performer | Studios | +|---|---| +| ![Performer](https://raw.githubusercontent.com/nexalapp/CommunityScripts/b03f96d614bb72c4df7a72e5227bcd8f2e04833f/Theme-Velour/desktop-performer.jpg) | ![Studios](https://raw.githubusercontent.com/nexalapp/CommunityScripts/b03f96d614bb72c4df7a72e5227bcd8f2e04833f/Theme-Velour/desktop-studios.jpg) | + +| Performers | Settings | +|---|---| +| ![Performers](https://raw.githubusercontent.com/nexalapp/CommunityScripts/b03f96d614bb72c4df7a72e5227bcd8f2e04833f/Theme-Velour/desktop-performers.jpg) | ![Settings](https://raw.githubusercontent.com/nexalapp/CommunityScripts/b03f96d614bb72c4df7a72e5227bcd8f2e04833f/Theme-Velour/desktop-settings.jpg) | + +Scenes on a phone A scene on a phone diff --git a/themes/Theme-Velour/Theme-Velour.yml b/themes/Theme-Velour/Theme-Velour.yml new file mode 100644 index 00000000..da3760c6 --- /dev/null +++ b/themes/Theme-Velour/Theme-Velour.yml @@ -0,0 +1,12 @@ +name: Theme - Velour +description: A velvet lounge. Deep plum, dusty rose actions, champagne flourishes, italic serif titles, framed cards. Self-hosted fonts; no external requests. +version: 0.1.0 +ui: + css: + - tokens.css + - base.css + - shell.css + - cards.css + - detail.css + assets: + /: assets diff --git a/themes/Theme-Velour/assets/fonts/InstrumentSerif-400.woff2 b/themes/Theme-Velour/assets/fonts/InstrumentSerif-400.woff2 new file mode 100644 index 00000000..17653042 Binary files /dev/null and b/themes/Theme-Velour/assets/fonts/InstrumentSerif-400.woff2 differ diff --git a/themes/Theme-Velour/assets/fonts/InstrumentSerif-400i.woff2 b/themes/Theme-Velour/assets/fonts/InstrumentSerif-400i.woff2 new file mode 100644 index 00000000..ce3c417c Binary files /dev/null and b/themes/Theme-Velour/assets/fonts/InstrumentSerif-400i.woff2 differ diff --git a/themes/Theme-Velour/assets/fonts/LICENSE.md b/themes/Theme-Velour/assets/fonts/LICENSE.md new file mode 100644 index 00000000..0414b72b --- /dev/null +++ b/themes/Theme-Velour/assets/fonts/LICENSE.md @@ -0,0 +1,8 @@ +# Fonts + +Served from this plugin, so the theme makes no external font requests. Each font is licensed under the SIL Open Font License 1.1; the full licence and copyright notice ship beside it. + +- **InstrumentSerif**: Copyright 2022 The Instrument Serif Project Authors (https://github.com/Instrument/instrument-serif) — `OFL-InstrumentSerif.txt` +- **Outfit**: Copyright 2021 The Outfit Project Authors (https://github.com/Outfitio/Outfit-Fonts) — `OFL-Outfit.txt` + +Latin subset, woff2, as distributed by Google Fonts. diff --git a/themes/Theme-Velour/assets/fonts/OFL-InstrumentSerif.txt b/themes/Theme-Velour/assets/fonts/OFL-InstrumentSerif.txt new file mode 100644 index 00000000..669ce791 --- /dev/null +++ b/themes/Theme-Velour/assets/fonts/OFL-InstrumentSerif.txt @@ -0,0 +1,93 @@ +Copyright 2022 The Instrument Serif Project Authors (https://github.com/Instrument/instrument-serif) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/themes/Theme-Velour/assets/fonts/OFL-Outfit.txt b/themes/Theme-Velour/assets/fonts/OFL-Outfit.txt new file mode 100644 index 00000000..723cd447 --- /dev/null +++ b/themes/Theme-Velour/assets/fonts/OFL-Outfit.txt @@ -0,0 +1,93 @@ +Copyright 2021 The Outfit Project Authors (https://github.com/Outfitio/Outfit-Fonts) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/themes/Theme-Velour/assets/fonts/Outfit-var.woff2 b/themes/Theme-Velour/assets/fonts/Outfit-var.woff2 new file mode 100644 index 00000000..48aae950 Binary files /dev/null and b/themes/Theme-Velour/assets/fonts/Outfit-var.woff2 differ diff --git a/themes/Theme-Velour/base.css b/themes/Theme-Velour/base.css new file mode 100644 index 00000000..c211902e --- /dev/null +++ b/themes/Theme-Velour/base.css @@ -0,0 +1,404 @@ +/* Buttons, form controls, menus, modals, tabs, toggles. */ + +/* ---------- Buttons ---------- */ +.btn { + font-family: var(--vel-font-body); + font-size: 13px; + font-weight: 500; + border-radius: var(--vel-r-ctl); + transition: background-color 0.15s, color 0.15s, box-shadow 0.15s, transform 0.15s var(--vel-ease); +} +.btn:active { + transform: scale(0.98); +} + +/* Primary actions: rose pill (controls elsewhere are rounded rectangles) */ +.btn-primary:not(.minimal), +.btn-success { + border-radius: 999px !important; + background-color: var(--vel-acc) !important; + border-color: var(--vel-acc) !important; + color: var(--vel-acc-ink) !important; + font-weight: 600; +} +.btn-primary:not(.minimal):hover, +.btn-success:hover { + background-color: var(--vel-acc-hover) !important; + border-color: var(--vel-acc-hover) !important; +} + +/* Secondary: neutral raised pill */ +.btn-secondary, +.btn-info, +.btn-light, +.btn-outline-secondary { + background-color: var(--vel-bg3) !important; + border-color: transparent !important; + color: var(--vel-t1) !important; +} +.btn-secondary:hover, +.btn-info:hover, +.btn-light:hover, +.btn-outline-secondary:hover { + background-color: var(--vel-bg4) !important; +} +.btn-secondary.active, +.btn-secondary:not(:disabled):not(.disabled):active { + background-color: var(--vel-bg4) !important; + color: var(--vel-acc) !important; +} + +/* Destructive: quiet outline until hovered */ +.btn-danger { + background-color: transparent !important; + border: 1px solid var(--vel-bg3) !important; + color: var(--vel-t2) !important; +} +.btn-danger:hover { + background-color: var(--vel-danger) !important; + border-color: var(--vel-danger) !important; + color: #fff !important; +} + +/* Icon-only "minimal" buttons */ +.btn.minimal { + background-color: transparent !important; + border-color: transparent !important; + color: var(--vel-t2) !important; + box-shadow: none !important; +} +.btn.minimal:hover { + color: var(--vel-t1) !important; + background-color: var(--vel-bg2) !important; +} + +.btn-link { + color: var(--vel-acc) !important; +} + +.btn:disabled, +.btn.disabled { + opacity: 0.45; +} + +.btn:focus, +.btn.focus { + box-shadow: none !important; +} +.btn:focus-visible { + box-shadow: var(--vel-ring) !important; +} + +/* ---------- Form controls ---------- */ +.form-control, +.custom-select, +.input-control, +.react-select__control, +.date-input, +.text-input { + background-color: var(--vel-bg2) !important; + border: 1px solid transparent !important; + color: var(--vel-t1) !important; + border-radius: 10px !important; + font-size: 13px; + box-shadow: none !important; +} +.form-control:focus, +.react-select__control--is-focused { + border-color: var(--vel-acc) !important; + box-shadow: 0 0 0 3px var(--vel-acc-soft) !important; +} +.form-control::placeholder, +.react-select__placeholder { + color: var(--vel-t3) !important; +} +.form-control:disabled, +.form-control[readonly] { + background-color: var(--vel-bg1) !important; + color: var(--vel-t2) !important; +} +textarea.form-control { + border-radius: 10px !important; +} + +/* Single-line inputs are pills */ +input.form-control:not([type="checkbox"]):not([type="range"]), +select.form-control, +.custom-select { + border-radius: var(--vel-r-ctl) !important; + padding-left: 14px; + padding-right: 14px; +} + +.input-group > .form-control:not(:last-child) { + border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important; +} +.input-group > .input-group-append > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.react-select__menu, +.react-select__menu-list { + background-color: var(--vel-bg2) !important; + border-radius: var(--vel-r-card) !important; + box-shadow: var(--vel-shadow-pop) !important; +} +.react-select__option { + background-color: transparent !important; + color: var(--vel-t2) !important; + border-radius: 7px; +} +.react-select__option--is-focused, +.react-select__option--is-selected { + background-color: var(--vel-bg3) !important; + color: var(--vel-t1) !important; +} +.react-select__multi-value { + background-color: var(--vel-bg3) !important; + border-radius: 999px !important; +} +.react-select__multi-value__label, +.react-select__single-value, +.react-select__input-container { + color: var(--vel-t1) !important; +} + +label, +.form-label, +.col-form-label { + color: var(--vel-t2); + font-size: 13px; + font-weight: 500; +} + +/* Checkboxes */ +input[type="checkbox"], +input[type="radio"] { + accent-color: var(--vel-acc); +} + +/* Toggles (Bootstrap custom-switch) */ +.custom-switch .custom-control-label::before { + background-color: var(--vel-bg3) !important; + border-color: var(--vel-bg3) !important; + border-radius: 999px !important; +} +.custom-switch .custom-control-label::after { + background-color: var(--vel-t3) !important; +} +.custom-switch .custom-control-input:checked ~ .custom-control-label::before { + background-color: var(--vel-acc) !important; + border-color: var(--vel-acc) !important; +} +.custom-switch .custom-control-input:checked ~ .custom-control-label::after { + background-color: var(--vel-acc-ink) !important; +} +.custom-control-input:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 3px var(--vel-acc-soft) !important; +} + +/* Range sliders */ +input[type="range"] { + accent-color: var(--vel-acc); +} + +/* ---------- Menus ---------- */ +.dropdown-menu, +.popover { + background-color: var(--vel-bg2) !important; + border: 1px solid var(--vel-line) !important; + border-radius: var(--vel-r-card) !important; + box-shadow: var(--vel-shadow-pop) !important; + padding: 6px !important; + color: var(--vel-t1); +} +.dropdown-item { + color: var(--vel-t2) !important; + border-radius: 7px; + padding: 6px 10px !important; + font-size: 13px; +} +.dropdown-item:hover, +.dropdown-item:focus { + background-color: var(--vel-bg3) !important; + color: var(--vel-t1) !important; +} +.dropdown-item.active, +.dropdown-item:active { + background-color: var(--vel-bg3) !important; + color: var(--vel-acc) !important; +} +.dropdown-divider { + border-color: var(--vel-line) !important; +} +.popover-header { + background: transparent !important; + border-color: var(--vel-line) !important; + color: var(--vel-t1); +} +.popover-body { + color: var(--vel-t1) !important; +} +.popover .arrow::after { + border-top-color: var(--vel-bg2) !important; + border-bottom-color: var(--vel-bg2) !important; +} + +.tooltip-inner { + background-color: var(--vel-bg3); + color: var(--vel-t1); + border-radius: 8px; + font-size: 12px; +} + +/* ---------- Modals ---------- */ +.modal-backdrop.show { + opacity: 1 !important; + background-color: rgba(0, 0, 0, 0.45) !important; + backdrop-filter: blur(2px); +} +.modal-content { + background-color: var(--vel-bg1) !important; + border: 1px solid var(--vel-line) !important; + border-radius: var(--vel-r-card) !important; + box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6); + color: var(--vel-t1); +} +.modal-header, +.modal-footer { + border-color: var(--vel-line) !important; + background-color: transparent !important; +} +.modal-header, +.modal-header .modal-title { + font-family: var(--vel-font-title); + font-style: var(--vel-title-style); + font-size: 22px; +} +.modal-body { + background-color: transparent !important; +} +.close { + color: var(--vel-t2) !important; + text-shadow: none !important; +} + +/* ---------- Tabs (underline) ---------- */ +.nav-tabs { + border-bottom: 1px solid var(--vel-line) !important; + gap: 18px; +} +.nav-tabs .nav-link { + background: transparent !important; + border: 0 !important; + border-bottom: 2px solid transparent !important; + border-radius: 0 !important; + color: var(--vel-t2) !important; + font-size: 13px; + font-weight: 500; + padding: 6px 0 9px !important; +} +.nav-tabs .nav-link:hover { + color: var(--vel-t1) !important; +} +.nav-tabs .nav-link.active { + color: var(--vel-t1) !important; + border-bottom-color: var(--vel-acc) !important; +} +.nav-tabs .nav-link .left-spacing, +.nav-tabs .badge, +.nav-link .count { + background-color: var(--vel-bg3) !important; + color: var(--vel-t1) !important; + border-radius: 999px !important; + font-size: 11px; + font-weight: 500; + padding: 1px 7px; +} + +/* Pills (settings menu) */ +.nav-pills .nav-link { + color: var(--vel-t2) !important; + border-radius: 999px !important; + font-size: 13px; + font-weight: 500; + padding: 7px 12px; +} +.nav-pills .nav-link:hover { + color: var(--vel-t1) !important; + background-color: var(--vel-bg2) !important; +} +.nav-pills .nav-link.active, +.nav-pills .show > .nav-link { + background-color: var(--vel-bg3) !important; + color: var(--vel-t1) !important; +} + +/* Badges and tag chips */ +.badge { + border-radius: 999px; + font-weight: 500; +} +.badge-secondary, +.tag-item, +.badge.tag-item { + background-color: var(--vel-bg3) !important; + color: var(--vel-t1) !important; + border-radius: 999px !important; + font-size: 12px; + font-weight: 500; + padding: 4px 10px; +} +.badge-primary, +.badge-info { + background-color: var(--vel-acc-soft) !important; + color: var(--vel-acc) !important; +} + +/* Generic panels */ +.card { + background-color: var(--vel-bg1); + border: 0; + border-radius: var(--vel-r-card); + box-shadow: none; + color: var(--vel-t1); +} +.card-header, +.card-footer { + background-color: transparent; + border-color: var(--vel-line); +} + +.table { + color: var(--vel-t1); +} +.table td, +.table th { + border-color: var(--vel-line) !important; +} + +/* Rating stars: off-white on, neutral off */ +.rating-stars .star-fill, +.rating-stars .filled, +.rating-stars button.star-fill svg { + color: var(--vel-rating) !important; +} +.rating-stars .unfilled, +.rating-stars button:not(.star-fill) svg { + color: var(--vel-bg4) !important; +} + +/* Toasts */ +.toast { + background-color: var(--vel-bg2) !important; + border: 1px solid var(--vel-line) !important; + border-radius: var(--vel-r-card) !important; + color: var(--vel-t1); +} +.toast-header { + background-color: transparent !important; + color: var(--vel-t1) !important; + border-color: var(--vel-line) !important; +} diff --git a/themes/Theme-Velour/cards.css b/themes/Theme-Velour/cards.css new file mode 100644 index 00000000..8ef2faf8 --- /dev/null +++ b/themes/Theme-Velour/cards.css @@ -0,0 +1,493 @@ +/* Cards. Everything is a framed plum tile: the image inset with 10px + corners, text below it, a rose glow on hover. + Stash sets each card's width inline from the zoom slider: never override + width here, only aspect and contents. */ + +.grid-card { + margin: 6px !important; + border: 0 !important; + border-radius: var(--vel-r-card) !important; + transition: transform 0.22s var(--vel-ease), box-shadow 0.22s var(--vel-ease); +} +.grid-card hr { + display: none; +} +.row.justify-content-center:has(> .grid-card) { + padding: 8px 10px 16px; +} + +/* Selection check: a ring on hover, a filled wine dot when checked */ +.grid-card .card-controls { + position: absolute; + top: 10px; + left: 10px; + z-index: 4; + opacity: 0; + transition: opacity 0.15s; +} +.grid-card:hover .card-controls, +.grid-card .card-controls:has(.card-check:checked) { + opacity: 1; +} +.grid-card .card-check { + -webkit-appearance: none; + appearance: none; + width: 20px !important; + height: 20px !important; + margin: 0 !important; + padding: 0 !important; + border-radius: 50% !important; + border: 2px solid var(--vel-t1) !important; + background: rgba(22, 22, 22, 0.35) !important; + cursor: pointer; +} +.grid-card .card-check:checked { + border-color: var(--vel-acc) !important; + background: var(--vel-acc) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E") center / 12px no-repeat !important; +} +.grid-card:has(.card-check:checked) { + box-shadow: var(--vel-ring) !important; +} + +/* Count buttons at the foot of a card */ +.grid-card .card-popovers { + justify-content: flex-start !important; + gap: 10px; + margin: 0 !important; + padding: 0 !important; + flex-wrap: nowrap; +} +.grid-card .card-popovers .btn { + padding: 0 !important; + height: auto; + font-size: 11px !important; + font-weight: 500; + color: var(--vel-t2) !important; + background: transparent !important; +} +.grid-card .card-popovers .btn:hover { + color: var(--vel-t1) !important; +} +.grid-card .card-popovers svg { + width: 11px; + margin-right: 4px; + opacity: 0.8; +} + +/* ================= Framed media cards: scenes, performers ================= + A plum tile with 5px of padding; the image sits inset with 10px corners + and the text runs below it. */ +.scene-card.grid-card, +.performer-card.grid-card { + position: relative; + overflow: hidden; + padding: 5px !important; + background-color: var(--vel-bg1) !important; +} +.scene-card.grid-card:hover, +.performer-card.grid-card:hover { + transform: translateY(-2px); + box-shadow: var(--vel-glow); +} + +.scene-card .thumbnail-section, +.performer-card .thumbnail-section { + position: relative !important; + margin: 0 !important; + border-radius: 10px; + overflow: hidden; + background: var(--vel-media); + max-height: none !important; +} +.scene-card .thumbnail-section { + aspect-ratio: 16 / 9; + height: auto !important; +} +.performer-card .thumbnail-section { + aspect-ratio: 2 / 3; + height: auto !important; +} +.scene-card .scene-card-link, +.scene-card .scene-card-preview, +.performer-card .thumbnail-section > a:first-child { + display: block; + height: 100% !important; + max-height: none !important; + aspect-ratio: auto !important; +} +.scene-card .scene-card-preview-image, +.scene-card .scene-card-preview-video, +.performer-card .performer-card-image { + width: 100% !important; + height: 100% !important; + max-height: none !important; + min-width: 0 !important; + aspect-ratio: auto !important; + object-fit: cover; + object-position: center top; + border-radius: 0 !important; +} + +/* Text below the image */ +.scene-card .card-section, +.performer-card .card-section { + padding: 10px 9px 4px !important; + margin: 0 !important; + background: transparent !important; + display: grid; + gap: 3px; +} +.scene-card .card-popovers, +.performer-card .card-popovers { + padding: 3px 9px 6px !important; +} +.scene-card .card-popovers .btn, +.performer-card .card-popovers .btn { + color: var(--vel-t3) !important; +} + +.scene-card .card-section-title, +.performer-card .card-section-title { + font-family: var(--vel-font-title) !important; + font-style: var(--vel-title-style); + font-size: var(--vel-title-card) !important; + line-height: var(--vel-title-lh) !important; + font-weight: 400 !important; + color: var(--vel-t1) !important; + text-shadow: none !important; + margin: 0 !important; +} +.scene-card .card-section-title .TruncatedText { + -webkit-line-clamp: 2 !important; +} +.scene-card .scene-card__details { + display: grid; + gap: 3px; + margin: 0 !important; +} +.scene-card .scene-card__date { + font-size: 11px; + font-weight: 500; + color: var(--vel-t3) !important; +} +.scene-card .scene-card__description { + font-size: 12px !important; + line-height: 1.4 !important; + color: var(--vel-t2) !important; + -webkit-line-clamp: 2 !important; + margin: 0 !important; +} +.scene-card .extra-scene-info { + display: none !important; +} + +/* "1080P · 21:04" in champagne, bottom right of the image */ +.scene-card .scene-specs-overlay { + position: absolute !important; + top: auto !important; + right: 8px !important; + bottom: 8px !important; + left: auto !important; + z-index: 3; + padding: 3px 7px; + border-radius: var(--vel-r-chip); + background: rgba(0, 0, 0, 0.55); + color: var(--vel-acc2) !important; + font-size: 10px !important; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; + text-shadow: none !important; + display: flex; +} +.scene-card .scene-specs-overlay > span { + font-size: 10px !important; + font-weight: 600 !important; + margin: 0 !important; + padding: 0 !important; + color: inherit !important; + text-shadow: none !important; +} +.scene-card .scene-specs-overlay .overlay-resolution + .overlay-duration::before { + content: "\00a0·\00a0"; +} + +/* Studio logo, top left of the image */ +.scene-card .studio-overlay { + position: absolute !important; + top: 8px !important; + left: 8px !important; + right: auto !important; + z-index: 3; + max-width: 45%; + padding: 3px 7px; + border-radius: var(--vel-r-chip); + background: rgba(0, 0, 0, 0.45); + opacity: 0.9; + height: auto !important; + display: flex; + align-items: center; +} +/* Stock gives the logo link a fixed height; let the chip wrap the logo */ +.scene-card .studio-overlay a { + display: block; + height: auto !important; + line-height: 0; +} +.scene-card .studio-overlay .image-thumbnail { + display: block; + height: 18px !important; + max-height: 18px !important; + max-width: 100%; + width: auto; + object-fit: contain; +} + +/* The selection check sits top right on these cards */ +.scene-card .card-controls, +.performer-card .card-controls { + left: auto !important; + right: 13px; + top: 13px; +} + +.scene-card .progress-bar { + z-index: 3; + background-color: var(--vel-acc) !important; +} + +/* Rating as a small chip */ +.grid-card .rating-banner { + transform: none !important; + left: 13px !important; + right: auto !important; + top: auto !important; + bottom: auto !important; + width: auto !important; + padding: 2px 7px !important; + border-radius: var(--vel-r-chip) !important; + background: rgba(0, 0, 0, 0.55) !important; + color: var(--vel-acc) !important; + font-size: 10px !important; + font-weight: 600; + letter-spacing: 0.06em; + z-index: 3; + box-shadow: none !important; +} + +/* ---- Performer specifics ---- */ +.performer-card .card-section-title { + display: flex !important; + align-items: baseline; + gap: 8px; +} +.performer-card .card-section-title .gender-icon, +.performer-card .card-section-title svg { + color: var(--vel-t3) !important; + width: 12px; + flex: none; +} +.performer-card .card-section-title span, +.performer-card .performer-name { + font-size: inherit !important; +} +.performer-card .performer-card__age { + font-size: 11px !important; + font-weight: 500; + color: var(--vel-t3) !important; +} +.performer-card .performer-card__country-flag { + display: none !important; +} +.performer-card .thumbnail-section > a:has(.performer-card__country-string) { + position: absolute; + right: 8px; + bottom: 8px; + height: auto !important; + z-index: 3; +} +.performer-card .performer-card__country-string { + display: inline-block !important; + padding: 3px 7px; + border-radius: var(--vel-r-chip); + background: rgba(0, 0, 0, 0.55); + color: var(--vel-t1); + font-size: 10px; + font-weight: 600; + letter-spacing: 0.08em; +} +/* Favourite heart: top-right, mirroring the selection check on the left, at + the same 20px. No black halo or pill; off-white until it's a favourite. */ +.grid-card button.btn.favorite-button { + position: absolute !important; + z-index: 4; + top: 10px !important; + right: 10px !important; + left: auto !important; + width: 20px; + height: 20px; + padding: 0 !important; + margin: 0 !important; + display: grid; + place-items: center; + background: none !important; + border: 0 !important; + box-shadow: none !important; + filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.45)) !important; + color: var(--vel-acc) !important; +} +.grid-card button.btn.favorite-button svg.fa-icon, +.grid-card button.btn.favorite-button svg { + width: 18px !important; + height: 18px !important; + font-size: 18px !important; + margin: 0 !important; +} +.grid-card button.btn.favorite-button.not-favorite { + color: rgba(243, 240, 238, 0.85) !important; +} +.grid-card button.btn.favorite-button:hover { + background: none !important; + transform: scale(1.1); +} + +/* ================= Framed tiles: studios, tags, galleries, groups ================= */ +.studio-card.grid-card, +.tag-card.grid-card, +.gallery-card.grid-card, +.group-card.grid-card, +.image-card.grid-card { + background-color: var(--vel-bg1) !important; + padding: 5px !important; + overflow: hidden; +} +.studio-card.grid-card:hover, +.tag-card.grid-card:hover, +.gallery-card.grid-card:hover, +.group-card.grid-card:hover, +.image-card.grid-card:hover { + transform: translateY(-2px); + box-shadow: var(--vel-glow); +} +.studio-card .thumbnail-section, +.tag-card .thumbnail-section, +.gallery-card .thumbnail-section, +.group-card .thumbnail-section, +.image-card .thumbnail-section { + border-radius: 10px; + overflow: hidden; + background-color: var(--vel-bg2); + position: relative; +} +/* Fixed 16:9 logo box. The logo is positioned inside it, so a tall logo + can't stretch the box and every tile in a grid comes out the same size. */ +.studio-card .studio-card-header, +.tag-card .tag-card-header { + display: block !important; + position: relative; + aspect-ratio: 16 / 9; + width: 100%; + height: auto !important; + min-height: 0 !important; + max-height: none !important; + overflow: hidden; + background-color: var(--vel-bg2); +} +.studio-card .studio-card-image, +.tag-card .tag-card-image { + position: absolute; + inset: 0; + width: 100% !important; + height: 100% !important; + max-width: none !important; + max-height: none !important; + min-height: 0 !important; + object-fit: contain; + padding: 10% 12%; + aspect-ratio: auto !important; +} +.gallery-card .gallery-card-image, +.group-card .group-card-image, +.image-card .image-card-preview-image { + border-radius: 0; + object-fit: cover; +} +.studio-card .card-section, +.tag-card .card-section, +.gallery-card .card-section, +.group-card .card-section, +.image-card .card-section { + padding: 10px 9px 4px !important; + background: transparent !important; +} +.studio-card .card-section-title, +.tag-card .card-section-title, +.gallery-card .card-section-title, +.group-card .card-section-title, +.image-card .card-section-title { + font-size: var(--vel-title-card) !important; + line-height: var(--vel-title-lh) !important; + color: var(--vel-t1) !important; + margin-bottom: 3px !important; + text-align: left !important; +} +.studio-card__details, +.studio-card__details a, +.tag-card .tag-description, +.gallery-card__details, +.group-card__details { + font-size: 11px !important; + color: var(--vel-t3) !important; + font-weight: 500; +} +.studio-card .card-popovers, +.tag-card .card-popovers, +.gallery-card .card-popovers, +.group-card .card-popovers, +.image-card .card-popovers { + padding: 2px 9px 6px !important; +} +.studio-card .card-popovers .btn, +.tag-card .card-popovers .btn, +.gallery-card .card-popovers .btn, +.group-card .card-popovers .btn { + color: var(--vel-t3) !important; +} + +/* Home-page carousels ("Recently added ...") size each card from its + contents, so a wide studio logo made a wide card. Pin them like stock. */ +.recommendation-row .grid-card { + width: 320px !important; + max-width: 320px; +} +.recommendation-row .studio-card .studio-card-image, +.recommendation-row .tag-card .tag-card-image { + max-width: 100% !important; +} + +/* Reserve the "Part of …" line so studio tiles stay the same height with or + without a parent studio. */ +.studio-card .studio-card__details { + min-height: 1.5em; +} + +/* Performer cards have no studio chip, so the check goes back top-left and + the favourite heart takes the top-right corner. */ +.performer-card .card-controls { + left: 13px !important; + right: auto !important; +} +.performer-card button.btn.favorite-button { + top: 8px !important; /* + the 5px tile padding = 13px, matching the check */ + right: 8px !important; +} + +/* Framed tiles place the heart inside the 5px-inset image box; offset it so + it sits as far from the corner as the check does. */ +.studio-card button.btn.favorite-button, +.tag-card button.btn.favorite-button, +.gallery-card button.btn.favorite-button, +.group-card button.btn.favorite-button { + top: 5px !important; + right: 5px !important; +} diff --git a/themes/Theme-Velour/detail.css b/themes/Theme-Velour/detail.css new file mode 100644 index 00000000..7a04e50b --- /dev/null +++ b/themes/Theme-Velour/detail.css @@ -0,0 +1,480 @@ +/* Scene page, entity detail headers, list/wall/tagger views, stats, + settings, empty states. */ + +/* Bootstrap utility Stash uses for menus and panels */ +.bg-secondary { + background-color: var(--vel-bg2) !important; +} +.dropdown-menu .bg-secondary.dropdown-item { + background-color: transparent !important; +} +.dropdown-menu .bg-secondary.dropdown-item:hover, +.dropdown-menu .bg-secondary.dropdown-item:focus { + background-color: var(--vel-bg3) !important; +} +.text-white { + color: var(--vel-t1) !important; +} + +/* ================= Scene page ================= */ +.scene-tabs { + background-color: var(--vel-bg1); + border-right: 1px solid var(--vel-line); + padding: 0 16px 16px !important; +} +.scene-tabs .scene-header-container .studio-logo { + margin: 0 -16px 18px !important; + padding: 16px; + min-height: 0; +} +.scene-tabs .scene-header-container img.studio-logo { + max-height: 100px; + object-fit: contain; +} +.scene-tabs .scene-header { + font-family: var(--vel-font-title) !important; + font-style: var(--vel-title-style); + font-size: var(--vel-title-page) !important; + line-height: 1.1 !important; + font-weight: 400; + color: var(--vel-t1); + margin-bottom: 6px; +} +.scene-subheader { + font-size: 13px; + color: var(--vel-t2); +} +.scene-subheader .resolution { + color: var(--vel-t1); + font-weight: 600; +} +.scene-subheader .divider { + opacity: 0; + margin: 0 2px; +} +.scene-subheader .frame-rate::after { + content: " ·"; +} +.scene-toolbar { + color: var(--vel-t2); + margin-top: 4px; +} +.scene-tabs .nav-tabs { + margin-top: 10px; +} +.scene-details h6, +.scene-file-info dt, +.scene-file-info dd, +.scene-details .detail-item { + font-family: var(--vel-font-body); + font-size: 13px !important; + font-weight: 400; + line-height: 1.6; + color: var(--vel-t2); +} +.scene-details a, +.scene-file-info a { + color: var(--vel-acc) !important; +} +.scene-description, +.scene-details .TruncatedText { + font-size: 13px; + line-height: 1.6; + color: var(--vel-t2); +} +.scene-file-info dt { + color: var(--vel-t1); + font-weight: 500; +} + +/* The collapse handle between the panel and the player */ +.scene-divider .btn { + background-color: transparent !important; + border: 0 !important; + color: var(--vel-t3) !important; + width: 14px; + padding: 0 !important; + border-radius: 0 !important; +} +.scene-divider .btn:hover { + background-color: var(--vel-bg2) !important; + color: var(--vel-t1) !important; +} + +/* Player */ +.scene-player-container { + background-color: var(--vel-bg0); +} +.video-js .vjs-play-progress, +.video-js .vjs-volume-level { + background-color: var(--vel-acc) !important; +} +.video-js .vjs-load-progress { + background: rgba(255, 255, 255, 0.25) !important; +} +.video-js .vjs-progress-holder { + background: rgba(255, 255, 255, 0.18) !important; + border-radius: 2px; +} +.video-js .vjs-control-bar { + background: linear-gradient(180deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.7)) !important; +} +.video-js .vjs-big-play-button { + width: 72px !important; + height: 72px !important; + line-height: 72px !important; + margin-left: -36px !important; + margin-top: -36px !important; + border: 0 !important; + border-radius: 50% !important; + background-color: rgba(255, 255, 255, 0.14) !important; + backdrop-filter: blur(6px); +} + +/* Scrubber strip under the player */ +.scrubber-wrapper { + gap: 8px; + padding: 10px 16px 12px; + margin: 0 !important; +} +.scrubber-wrapper .scrubber-button { + width: 22px !important; + min-width: 22px; + padding: 0 !important; + border: 0 !important; + border-radius: var(--vel-r-chip) !important; + background-color: var(--vel-bg2) !important; + color: var(--vel-acc) !important; + font-weight: 400; +} +.scrubber-wrapper .scrubber-button:hover { + background-color: var(--vel-bg3) !important; +} +.scrubber-content { + border-radius: var(--vel-r-chip); + background-color: var(--vel-bg1); + margin: 0 !important; +} +.scrubber-tags-background { + background-color: var(--vel-bg3) !important; +} +#scrubber-current-position { + background-color: var(--vel-t1) !important; +} +.scrubber-tag { + background-color: var(--vel-acc) !important; + color: var(--vel-acc-ink) !important; + border-radius: 4px; +} + +/* Edit panels: Save stands out, the rest stay neutral */ +.details-edit .btn-primary:not(.edit):not(.save), +.scraper-menu .btn-primary, +.search-item .btn-primary:not(.minimal):not(.input-group-append .btn), +.setting-section .btn-primary:not(.minimal), +#settings-menu-container .btn-primary { + border-radius: var(--vel-r-ctl) !important; + background-color: var(--vel-bg3) !important; + border-color: transparent !important; + color: var(--vel-t1) !important; + font-weight: 500; +} +#scene-edit-details .edit-buttons-container, +.edit-buttons-container { + background-color: var(--vel-bg1) !important; +} +.form-group .col-form-label, +.form-group label { + color: var(--vel-t2); +} + +/* ================= Detail headers: performer, studio, tag, group ================= */ +.detail-header, +.detail-header.full-width { + background-color: var(--vel-bg1) !important; + border-bottom: 1px solid var(--vel-line); + color: var(--vel-t1); +} +.detail-header .background-image-container { + opacity: 0.18; +} +.detail-header-image { + position: relative; +} +.detail-header-image .btn-link { + padding: 0 !important; +} +.detail-header-image img.performer, +.detail-header-image img { + border-radius: 0 !important; + box-shadow: none !important; +} +.detail-header h2, +.detail-header .performer-name, +.detail-header .studio-name, +.detail-header .tag-name { + font-family: var(--vel-font-title); + font-style: var(--vel-title-style); + font-size: 40px; + line-height: 1; + font-weight: 400; + color: var(--vel-t1); +} +.detail-header .alias-head { + font-size: 13px; + color: var(--vel-t2); +} +.detail-header .detail-item { + font-size: 13px; + line-height: 1.75; +} +.detail-header .detail-item-title { + color: var(--vel-t3); + font-weight: 500; + width: 130px; + display: inline-block; +} +.detail-header .detail-item-value { + color: var(--vel-t1); +} +.detail-header .details-edit { + gap: 8px; + margin-top: 10px; +} +.detail-header .name-icons .favorite-button:not(.not-favorite) { + color: var(--vel-acc) !important; +} +.detail-header .name-icons .link, +.detail-header a { + color: var(--vel-acc); +} + +/* StashDB / stash-box ID chips */ +.stash-id-pill, +.stash-id-pill span, +.stash-id-pill a { + border-radius: 999px !important; +} +.stash-id-pill span:first-child { + background-color: var(--vel-acc-soft) !important; + color: var(--vel-acc) !important; +} +.stash-id-pill span:last-child, +.stash-id-pill a { + background-color: var(--vel-bg3) !important; + color: var(--vel-t1) !important; +} + +/* Tabs under a detail header, centred */ +.detail-body .nav-tabs, +.performer-tabs .nav-tabs, +.studio-tabs .nav-tabs, +.tag-tabs .nav-tabs { + justify-content: center; + gap: 22px; +} + +/* Sticky header shown when scrolled */ +.sticky.detail-header { + background-color: rgba(30, 30, 30, 0.92) !important; + backdrop-filter: blur(10px); +} +.sticky.detail-header .sticky.detail-header-group a, +.sticky.detail-header .sticky.detail-header-group span { + color: var(--vel-t1) !important; +} + +/* ================= List (table) view ================= */ +.table-list table { + color: var(--vel-t1); +} +.table-list table thead th { + font-size: 12px; + font-weight: 600; + color: var(--vel-t2) !important; + border-bottom: 1px solid var(--vel-line) !important; + border-top: 0 !important; +} +.table-list table tbody tr { + border-bottom: 1px solid var(--vel-line); +} +.table-list table tbody tr:hover { + background-color: var(--vel-bg1); +} +.table-list table td { + vertical-align: middle; + border: 0 !important; + font-size: 13px; +} +.table-list table td img { + border-radius: 8px; +} +.table-list .title-link, +.table-list td.title-data a, +.table-list td a.performer-name { + font-family: var(--vel-font-title); + font-style: var(--vel-title-style); + font-size: 16px; + color: var(--vel-t1) !important; +} +.table-list td a { + color: var(--vel-t1); +} + +/* ================= Wall view ================= */ +.wall-item, +.wall-item-container { + border-radius: var(--vel-r-chip) !important; + overflow: hidden; +} +.wall-item-text, +.wall-item .wall-item-text { + background: linear-gradient(180deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.8)) !important; + font-family: var(--vel-font-title); + font-style: var(--vel-title-style); + font-size: 18px; + color: var(--vel-t1) !important; + text-shadow: none !important; +} + +/* ================= Tagger ================= */ +.tagger-container .search-item, +.tagger-container .tagger-item, +.search-item { + background-color: var(--vel-bg1) !important; + border: 0 !important; + border-radius: var(--vel-r-card) !important; + padding: 14px !important; + margin-bottom: 12px; +} +.tagger-container .search-item .scene-card-link img, +.search-item img { + border-radius: 10px; +} +.tagger-container .input-group-prepend .input-group-text, +.input-group-text { + background-color: var(--vel-bg3) !important; + border: 0 !important; + color: var(--vel-t2) !important; + font-size: 12px; + font-weight: 500; +} +.tagger-container .input-group .form-control { + border-radius: 0 !important; +} +.tagger-container .input-group > :first-child, +.tagger-container .input-group > :first-child .input-group-text { + border-radius: var(--vel-r-ctl) 0 0 var(--vel-r-ctl) !important; +} +.tagger-container .input-group .input-group-append .btn { + border-radius: 0 var(--vel-r-ctl) var(--vel-r-ctl) 0 !important; +} + +/* ================= Stats ================= */ +#stats-page .stats-element, +.stats .stats-element { + text-align: center; +} +#stats-page .stats-element .title, +.stats .title { + font-family: var(--vel-font-title) !important; + font-style: var(--vel-title-style); + font-size: 52px !important; + line-height: 1; + font-weight: 400 !important; + color: var(--vel-t1) !important; +} +#stats-page .stats-element .heading, +.stats .heading { + margin-top: 12px; + font-size: 11px !important; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--vel-t3) !important; + font-weight: 500; +} + +/* ================= Settings ================= */ +#settings-menu-container .nav-pills, +.settings-menu-container .nav-pills { + gap: 2px; +} +#settings-container h1, +#settings-container h2, +#settings-container h3, +.setting-section h1 { + font-family: var(--vel-font-title); + font-style: var(--vel-title-style); + font-size: 28px; + font-weight: 400; + color: var(--vel-t1); +} +.setting-section .card, +#settings-container .card, +#settings-container .setting-group { + background-color: var(--vel-bg1) !important; + border-radius: var(--vel-r-card) !important; + border: 0 !important; +} +.setting-section .setting, +#settings-container .setting { + border-color: var(--vel-line) !important; +} +.setting-section .setting h3, +.setting-section .setting-group h3, +#settings-container .setting h3, +#settings-container .setting-group h3, +#settings-container .setting-section h3, +.setting-section h3 { + font-family: var(--vel-font-body); + font-style: normal !important; + font-size: 15px; + font-weight: 600; +} +.setting .sub-heading, +.setting-section .sub-heading, +#settings-container .sub-heading { + font-size: 12px; + color: var(--vel-t2) !important; +} +.job-table.card { + background-color: var(--vel-bg1) !important; + border-radius: var(--vel-r-card) !important; + color: var(--vel-t2); +} +#tasks-panel .tasks-panel-queue h1 { + font-family: var(--vel-font-title); + font-style: var(--vel-title-style); +} +.logs .log-entry, +.LogEntry, +#log-entries { + font-size: 12px; +} + +/* ================= Empty list ================= */ +.item-list-container .sidebar-pane-content:not(:has(.grid-card, .table-list, .scene-wall, .wall, .tagger-container, .card, [class*="wall"])) > .pagination-index-container::after { + content: "Nothing here yet"; + display: block; + margin: 18vh auto 0; + text-align: center; + font-family: var(--vel-font-title); + font-style: var(--vel-title-style); + font-size: 26px; + color: var(--vel-t2); +} + +/* Big play button: Stash draws the icon at 100px, larger than the 72px circle, + so it overflowed off-centre. Size the glyph to the circle and nudge it right + so the triangle looks optically centred. */ +.video-js .vjs-big-play-button { + font-size: 36px !important; + border: 0 !important; +} +.video-js .vjs-big-play-button .vjs-icon-placeholder::before { + font-size: 36px !important; + line-height: 72px !important; + width: 100% !important; + height: 100% !important; + text-indent: 3px; +} diff --git a/themes/Theme-Velour/shell.css b/themes/Theme-Velour/shell.css new file mode 100644 index 00000000..4ccd2dc9 --- /dev/null +++ b/themes/Theme-Velour/shell.css @@ -0,0 +1,422 @@ +/* Top bar, list toolbar, pagination, result count, filter sidebar. */ + +/* ---------- Top bar ---------- */ +.top-nav, +.top-nav.bg-dark, +.top-nav .bg-dark { + background-color: var(--vel-bg1) !important; +} +.top-nav { + border-bottom: 1px solid var(--vel-line); + box-shadow: none !important; + padding-left: 16px !important; + padding-right: 16px !important; +} + +.top-nav .navbar-brand .brand-link { + font-family: var(--vel-font-title); + font-style: var(--vel-title-style); + font-size: 22px !important; + line-height: 1; + color: var(--vel-t1) !important; + background: transparent !important; + padding: 0 18px 0 0 !important; + letter-spacing: 0; +} +.top-nav .navbar-brand .brand-link:hover { + background: transparent !important; +} + +@media (min-width: 1200px) { + .top-nav .navbar-nav .nav-link { + padding: 0 1px !important; + } + .top-nav .navbar-nav .nav-link > a.btn { + padding: 6px 12px !important; + border-radius: 999px !important; + font-size: 13px; + font-weight: 500; + color: var(--vel-t2) !important; + } + .top-nav .navbar-nav .nav-link > a.btn:hover { + background-color: var(--vel-bg2) !important; + color: var(--vel-t1) !important; + } + .top-nav .navbar-nav .nav-link > a.btn.active { + background-color: var(--vel-bg3) !important; + color: var(--vel-t1) !important; + } + /* Words, not icons, in the desktop bar */ + .top-nav .navbar-nav .nav-link > a.btn .nav-menu-icon { + display: none !important; + } +} + +.top-nav .nav-utility .btn, +.top-nav .navbar-buttons .btn.minimal, +.top-nav .logout-button { + color: var(--vel-t2) !important; + border-radius: 999px !important; +} +.top-nav .navbar-buttons a[href$="/new"] .btn { + height: 32px; + padding: 0 14px; + border-radius: 999px !important; +} + +/* Theme Switch's own menu */ +.theme-plugin-menu .card, +.theme-plugin-menu .card-header, +.theme-plugin-menu .modal-header { + background-color: transparent !important; + color: var(--vel-t1) !important; + border-color: var(--vel-line) !important; + font-family: var(--vel-font-body); + font-size: 13px; +} + +/* ---------- Page container ---------- */ +.main.container-fluid { + background: transparent; +} + +/* ---------- List toolbar ---------- */ +.filtered-list-toolbar { + background-color: transparent !important; + padding-top: 12px !important; + padding-bottom: 0 !important; + gap: 8px !important; +} +.filtered-list-toolbar > * { + margin: 0 !important; +} + +/* Grouped controls become one pill with segments inside */ +.filtered-list-toolbar .btn-group, +.filtered-list-toolbar .sort-by-select { + background-color: var(--vel-bg2); + border-radius: var(--vel-r-ctl); + padding: 3px; + gap: 2px; + height: var(--vel-ctl-h); + align-items: center; + border: 0 !important; +} +.filtered-list-toolbar .btn-group .btn-group, +.filtered-list-toolbar .list-operations .btn-group .btn-group { + padding: 0; + background: transparent; + height: auto; +} +.filtered-list-toolbar .btn-group > .btn, +.filtered-list-toolbar .btn-group > .btn-group > .btn, +.filtered-list-toolbar .btn-group > .dropdown > .btn, +.filtered-list-toolbar .sort-by-select .btn { + background-color: transparent !important; + border: 0 !important; + border-radius: var(--vel-r-in) !important; + color: var(--vel-t2) !important; + height: 28px; + min-width: 30px; + padding: 0 9px; + display: inline-flex; + align-items: center; + justify-content: center; + margin: 0 !important; +} +.filtered-list-toolbar .btn-group .btn:hover, +.filtered-list-toolbar .sort-by-select .btn:hover { + color: var(--vel-t1) !important; + background-color: var(--vel-bg3) !important; +} +.filtered-list-toolbar .btn-group .btn.active { + background-color: var(--vel-bg3) !important; + color: var(--vel-acc) !important; +} + +/* Sort: label, hairline, direction */ +.filtered-list-toolbar .sort-by-select .input-group-prepend .btn { + color: var(--vel-t1) !important; + font-weight: 500; + padding: 0 12px; +} +.filtered-list-toolbar .sort-by-select .input-group-prepend { + margin: 0; + border-right: 1px solid var(--vel-line); +} +.filtered-list-toolbar .sort-by-select .dropdown-toggle::after { + display: none; +} + +/* Search */ +.filtered-list-toolbar .search-term-input .form-control, +.filtered-list-toolbar .search-term-input input { + height: var(--vel-ctl-h); + width: 220px; + padding-left: 34px !important; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%236f6a66' stroke-width='2.4' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M20 20l-3.5-3.5'/%3E%3C/svg%3E") !important; + background-repeat: no-repeat !important; + background-position: 13px center !important; +} + +/* Page size */ +.filtered-list-toolbar .page-size-selector select { + height: var(--vel-ctl-h); + font-weight: 500; + border: 0 !important; +} + +/* Zoom slider: hairline track, light knob */ +.filtered-list-toolbar .zoom-slider-container { + margin-left: 4px !important; +} +input[type="range"].zoom-slider { + -webkit-appearance: none; + appearance: none; + background: transparent !important; + width: 70px; + max-width: 70px; +} +input[type="range"].zoom-slider::-webkit-slider-runnable-track { + height: 3px; + border-radius: 2px; + background: var(--vel-bg3) !important; +} +input[type="range"].zoom-slider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 13px; + height: 13px; + margin-top: -5px; + border-radius: 50%; + background: var(--vel-t1) !important; + border: 0; +} +input[type="range"].zoom-slider::-moz-range-track { + height: 3px; + border-radius: 2px; + background: var(--vel-bg3) !important; +} +input[type="range"].zoom-slider::-moz-range-progress { + height: 3px; + border-radius: 2px; + background: var(--vel-acc); +} +input[type="range"].zoom-slider::-moz-range-thumb { + width: 13px; + height: 13px; + border-radius: 50%; + background: var(--vel-t1) !important; + border: 0; +} + +/* Filter-sidebar toggle */ +.sidebar-toggle-button-container .sidebar-toggle-button { + width: var(--vel-ctl-h); + height: var(--vel-ctl-h); + padding: 0 !important; + margin-left: 0 !important; + border: 0 !important; + border-radius: var(--vel-r-ctl) !important; + background-color: var(--vel-bg2) !important; + color: var(--vel-t2) !important; + opacity: 1 !important; + top: calc(48.75px + 12px) !important; + display: grid; + place-items: center; +} +.sidebar-toggle-button-container .sidebar-toggle-button:hover { + color: var(--vel-t1) !important; + background-color: var(--vel-bg3) !important; +} + +/* Selection toolbar: sticky over the grid, so it needs a solid backing */ +.item-list-container .filtered-list-toolbar.has-selection { + background-color: var(--vel-bg0) !important; + border: 1px solid var(--vel-line); + border-radius: var(--vel-r-card); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35); + padding: 6px 8px !important; +} +.filtered-list-toolbar.has-selection .selected-items-info, +.filtered-list-toolbar.has-selection .selected-items-info span { + color: var(--vel-acc) !important; + font-weight: 600; +} +.filtered-list-toolbar .btn.btn-danger, +.filtered-list-toolbar .btn-group > .btn.btn-danger { + background-color: var(--vel-danger) !important; + color: #fff !important; +} + +/* ---------- Pagination ---------- */ +.pagination-index-container { + margin-top: 10px; + display: flex; + flex-direction: column; + align-items: center; +} +.pagination.btn-group { + width: auto !important; + flex: none !important; + background-color: var(--vel-bg2); + border-radius: var(--vel-r-ctl); + padding: 3px; + gap: 2px; + margin-bottom: 0 !important; + box-shadow: none !important; +} +.pagination .btn, +.pagination .page-count-container .btn { + background-color: transparent !important; + border: 0 !important; + border-radius: var(--vel-r-in) !important; + color: var(--vel-t2) !important; + height: 26px; + min-width: 28px; + padding: 0 8px !important; + font-size: 12px; + font-weight: 500; + display: inline-flex; + align-items: center; + justify-content: center; +} +.pagination .btn:hover:not(:disabled) { + background-color: var(--vel-bg3) !important; + color: var(--vel-t1) !important; +} +.pagination .btn:disabled { + color: var(--vel-t3) !important; + opacity: 1; +} +.pagination .page-count { + color: var(--vel-t1) !important; + padding: 0 4px 0 10px !important; +} +.pagination .btn.active, +.pagination .btn-secondary.active { + background-color: var(--vel-bg3) !important; + color: var(--vel-t1) !important; +} +.pagination-footer .pagination.btn-group { + box-shadow: var(--vel-shadow-pop) !important; + border: 1px solid var(--vel-line); +} + +/* "1–40 of 6,715 · 3M 1W 3D · 7.9 TiB" */ +.paginationIndex { + display: block !important; + text-align: center; + margin: 8px 0 0 !important; + font-size: 11px !important; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--vel-t3) !important; +} +.paginationIndex br { + display: none; +} +.paginationIndex .scenes-stats, +.paginationIndex .images-stats { + font-size: 0; + color: var(--vel-t3); +} +.paginationIndex .scenes-stats > span, +.paginationIndex .images-stats > span { + font-size: 11px; +} +.paginationIndex .scenes-stats > span::before, +.paginationIndex .images-stats > span::before { + content: " · "; +} + +/* ---------- Filter sidebar ---------- */ +.sidebar-pane .sidebar, +.sidebar .sidebar-footer, +.sidebar-toolbar { + background-color: var(--vel-bg1) !important; +} +.sidebar-pane .sidebar { + border-right: 1px solid var(--vel-line); + padding: 12px 12px 0 !important; +} +.sidebar .sidebar-section, +.sidebar .sidebar-list-filter { + border: 0 !important; +} +.sidebar .collapse-button, +.sidebar .sidebar-section-header, +.sidebar .btn.minimal { + color: var(--vel-t1) !important; + font-size: 13px; + font-weight: 500; + border-radius: 8px !important; +} +.sidebar .collapse-button:hover { + background-color: var(--vel-bg2) !important; +} +.sidebar .collapse-button svg { + color: var(--vel-t3); +} + +/* Tables (list view, package manager) */ +.table-list table thead, +.package-manager table thead { + background-color: var(--vel-bg0) !important; +} +#tasks-panel .tasks-panel-queue { + background-color: var(--vel-bg0) !important; +} + +/* ---------- Rows under the toolbar: filter chips, pagination, count ---------- + One 10px rhythm between toolbar, chips, pagination and grid. On wide + screens the result count leaves its own line and sits right-aligned on the + first row under the toolbar (the filter chips if any, else pagination). */ +.sidebar-pane-content > .filter-tags { + margin: 10px 0 0 !important; + padding: 0 16px; + justify-content: center; +} +.sidebar-pane-content > .pagination-index-container { + margin-top: 10px !important; +} +.sidebar-pane-content > .pagination-index-container:not(:has(.pagination)) { + margin-top: 0 !important; +} +.row.justify-content-center:has(> .grid-card) { + padding-top: 10px !important; +} +@media (min-width: 1200px) { + .sidebar-pane-content { + position: relative; + } + .sidebar-pane-content > .pagination-index-container > .paginationIndex { + position: absolute; + right: 16px; + top: 49px; /* toolbar bottom (39px) + the 10px gap */ + height: 32px; + margin: 0 !important; + display: flex !important; + align-items: center; + white-space: nowrap; + } + /* keep centred chips clear of the count on the right */ + .sidebar-pane-content > .filter-tags { + padding: 0 190px; + } + /* nothing else on the row: hold the space so the grid doesn't slide under the count */ + .sidebar-pane-content:not(:has(> .filter-tags)) > .pagination-index-container:not(:has(.pagination)) { + min-height: 32px; + margin-top: 10px !important; + } +} +@media (min-width: 1200px) { + /* on the chip row, match the chips' height so the text centres on them */ + .sidebar-pane-content:has(> .filter-tags) > .pagination-index-container > .paginationIndex { + height: 24px; + } +} +/* flex grids (studios, tags, performers) carry a 6px card margin; trim the + row padding so every grid starts 10px below the row above */ +.row.justify-content-center:has(> .grid-card:not(.scene-card)) { + padding-top: 4px !important; +} diff --git a/themes/Theme-Velour/tokens.css b/themes/Theme-Velour/tokens.css new file mode 100644 index 00000000..bcc10eae --- /dev/null +++ b/themes/Theme-Velour/tokens.css @@ -0,0 +1,140 @@ +/* Velour: a velvet lounge. Deep plum that reads as black until lit; dusty + rose for actions, champagne for flourishes; italic serif titles over a + quiet sans. Framed 16:9 cards. + + Fonts are self-hosted (assets/fonts, OFL) so pages make no external + requests. The yml maps the plugin's assets/ folder to + /plugin/Theme-Velour/assets/; font URLs are relative to the stylesheet. */ + +@font-face { + font-family: "Outfit"; + font-style: normal; + font-weight: 400 600; + font-display: swap; + src: url("assets/fonts/Outfit-var.woff2") format("woff2"); +} +@font-face { + font-family: "Instrument Serif"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url("assets/fonts/InstrumentSerif-400.woff2") format("woff2"); +} +@font-face { + font-family: "Instrument Serif"; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url("assets/fonts/InstrumentSerif-400i.woff2") format("woff2"); +} + +:root { + /* Surfaces, darkest to lightest */ + --vel-bg0: #17121a; + --vel-bg1: #1f1922; + --vel-bg2: #28212c; + --vel-bg3: #352b3a; + --vel-bg4: #413546; + --vel-line: rgba(255, 255, 255, 0.07); + --vel-line-strong: rgba(255, 255, 255, 0.12); + + /* Text */ + --vel-t1: #f6eef0; + --vel-t2: #b9a9b1; + --vel-t3: #7f707a; + + /* Accent: dusty rose for actions, champagne for flourishes */ + --vel-acc2: #d9b36b; + --vel-acc: #d97a8c; + --vel-acc-hover: #e48d9d; + --vel-acc-ink: #2a0d14; + --vel-acc-soft: rgba(217, 122, 140, 0.16); + --vel-danger: #c9574f; + --vel-rating: #d97a8c; + + /* Type */ + --vel-font-body: "Outfit", system-ui, -apple-system, "Segoe UI", sans-serif; + --vel-font-title: "Instrument Serif", Georgia, "Times New Roman", serif; + --vel-title-style: italic; + --vel-title-card: 19px; + --vel-title-page: 34px; + --vel-title-lh: 1.1; + + /* Shape */ + --vel-r-ctl: 10px; /* buttons, inputs, control groups */ + --vel-r-in: 7px; /* segments inside a control group */ + --vel-r-card: 14px; + --vel-r-panel: 14px; + --vel-r-chip: 6px; + --vel-ctl-h: 34px; + + /* Depth and motion */ + --vel-shadow-pop: 0 16px 40px rgba(0, 0, 0, 0.55); + --vel-glow: 0 10px 28px rgba(217, 122, 140, 0.16); + --vel-ring: 0 0 0 2px var(--vel-acc); + --vel-ease: cubic-bezier(0.2, 0.8, 0.2, 1); + --vel-media: repeating-linear-gradient(135deg, #2c242f 0 8px, #261f29 8px 16px); +} + +html, +body { + background-color: var(--vel-bg0) !important; + color: var(--vel-t1); + font-family: var(--vel-font-body); + -webkit-font-smoothing: antialiased; +} + +body { + font-size: 14px; + line-height: 1.45; +} + +a { + color: var(--vel-t1); +} +a:hover { + color: #fff; +} + +h1, h2, h3, h4, +.card-section-title, +.modal-title { + font-family: var(--vel-font-title); + font-style: var(--vel-title-style); + font-weight: 400; + letter-spacing: 0; +} + +::selection { + background: var(--vel-acc-soft); +} + +/* Quiet scrollbars */ +* { + scrollbar-color: var(--vel-bg3) transparent; + scrollbar-width: thin; +} +::-webkit-scrollbar { + width: 10px; + height: 10px; +} +::-webkit-scrollbar-track { + background: transparent; +} +::-webkit-scrollbar-thumb { + background: var(--vel-bg3); + border: 3px solid var(--vel-bg0); + border-radius: 999px; +} + +:focus-visible { + outline: 2px solid var(--vel-acc) !important; + outline-offset: 2px; +} + +.text-muted { + color: var(--vel-t3) !important; +} +hr { + border-color: var(--vel-line); +}