ui: redesign from scratch

This commit is contained in:
4cuck authored and GitHub committed 2026-04-17 13:26:48 -04:00
1 parent 7321ee27bf
commit 1eb036d196
22 files changed
+1879 -936

No files matched your search

+37 -19
View File
@@ -16,46 +16,64 @@
}
.error-panel {
padding: 12px;
border-radius: 4px;
padding: 16px;
border-radius: var(--shape-lg);
display: flex;
background: var(--error_red);
box-shadow: 0 0 15px var(--shadow_dark);
background: var(--tp-error-container);
color: var(--tp-on-error-container);
box-shadow: var(--elevation-1);
margin: auto;
margin-top: -50px;
text-align: center;
font-weight: 500;
}
.search-bar > form {
padding: 12px;
border-radius: 4px;
padding: 12px 16px;
border-radius: var(--shape-md);
display: flex;
background: var(--darkest_grey);
box-shadow: 0 0 15px var(--shadow_dark);
background: var(--tp-surface-container-highest);
border: none;
box-shadow: none;
margin: auto;
margin-top: -50px;
transition: background 0.15s ease;
& button {
background: var(--bg_elements);
color: var(--fg_color);
background: var(--tp-primary);
color: var(--tp-on-primary);
border: 0;
border-radius: 3px;
border-radius: var(--shape-md);
cursor: pointer;
font-weight: bold;
width: 30px;
height: 30px;
padding: 0px 5px 1px 8px;
font-weight: 500;
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.15s ease;
/*box-shadow: var(--elevation-1);*/
&:hover {
background: var(--tp-on-primary-container);
/* box-shadow: var(--elevation-2); */
}
}
& input {
font-size: 16px;
width: 100%;
background: var(--bg_elements);
color: var(--fg_color);
background: var(--tp-surface-container-highest) !important;
color: var(--tp-on-surface);
border: 0;
border-radius: 4px;
padding: 4px;
border-radius: 10px;
padding: 4px 12px;
margin-right: 8px;
height: unset;
box-shadow: none !important;
&::placeholder {
color: var(--tp-on-surface-variant);
}
}
}
+422 -117
View File
@@ -1,64 +1,86 @@
button {
background-color: var(--bg_elements);
color: var(--fg_color);
border: 1px solid var(--accent_border);
padding: 3px 6px;
background-color: var(--tp-primary-container);
color: var(--tp-on-primary-container);
border: 0;
padding: 8px 16px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
float: right;
/* border-radius: var(--shape-md); */
transition: background 0.15s ease;
}
&:hover {
border-color: var(--accent);
}
button:hover {
background: var(--tp-primary);
color: var(--tp-on-primary);
}
&:active {
border-color: var(--accent_light);
}
button:active {
opacity: 0.85;
}
.translate-btn,
.translate-btn:hover,
.translate-btn:active {
background: none;
border: none;
border-radius: 0;
box-shadow: none;
padding: 0;
font-size: 0.78em;
color: var(--fg_faded);
transform: none;
transition: none;
}
input[type="text"],
input[type="date"],
input[type="number"],
select {
background-color: var(--bg_elements);
padding: 1px 4px;
color: var(--fg_color);
border: 1px solid var(--accent_border);
border-radius: 0;
background-color: var(--tp-surface-container-highest);
padding: 10px 14px;
color: var(--tp-on-surface);
border: 1px solid var(--tp-outline);
border-radius: var(--shape-md);
font-size: 14px;
transition: all 0.15s ease;
width: 100%;
box-sizing: border-box;
}
&:hover {
border-color: var(--accent);
}
input[type="text"]:hover,
input[type="date"]:hover,
input[type="number"]:hover,
select:hover {
border-color: var(--tp-on-surface-variant);
}
&:active {
border-color: var(--accent_light);
}
input[type="text"]:focus,
input[type="date"]:focus,
input[type="number"]:focus,
select:focus {
border-color: var(--tp-primary);
background: var(--tp-surface-container-highest);
box-shadow: 0 0 0 3px var(--tp-primary-container);
}
input[type="number"] {
-moz-appearance: textfield;
}
input[type="text"],
input[type="number"] {
height: 16px;
}
select {
height: 20px;
padding: 0 2px;
line-height: 1;
cursor: pointer;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23666' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 12px center;
padding-right: 32px;
}
input[type="date"]::-webkit-inner-spin-button {
display: none;
}
input[type="number"] {
-moz-appearance: textfield;
}
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
display: none;
@@ -79,139 +101,422 @@ input::-webkit-calendar-picker-indicator {
input::-webkit-datetime-edit-day-field:focus,
input::-webkit-datetime-edit-month-field:focus,
input::-webkit-datetime-edit-year-field:focus {
background-color: var(--accent);
color: var(--fg_color);
background-color: var(--tp-primary);
color: var(--tp-on-primary);
outline: none;
}
.date-range {
& .date-input {
display: inline-block;
position: relative;
}
.date-range .date-input {
display: inline-block;
position: relative;
}
& .icon-container {
pointer-events: none;
position: absolute;
top: 2px;
right: 5px;
}
.date-range .icon-container {
pointer-events: none;
position: absolute;
top: 2px;
visibility: hidden;
right: 5px;
}
& .search-title {
margin: 0 2px;
}
.date-range .search-title {
margin: 0 2px;
}
.icon-button button {
color: var(--accent);
color: var(--tp-primary);
text-decoration: none;
background: none;
border: none;
float: none;
padding: unset;
padding-left: 4px;
}
&:hover {
color: var(--accent_light);
}
.icon-button button:hover {
color: var(--tp-on-primary-container);
}
.checkbox {
position: absolute;
top: 1px;
top: 50%;
right: 0;
height: 17px;
width: 17px;
background-color: var(--bg_elements);
border: 1px solid var(--accent_border);
transform: translateY(-50%);
height: 20px;
width: 20px;
background-color: var(--tp-surface-container-highest);
border: 2px solid var(--tp-outline);
border-radius: var(--shape-xs);
transition: all 0.15s ease;
}
&:after {
content: "";
position: absolute;
display: none;
}
.checkbox:after {
content: "";
position: absolute;
display: none;
}
.checkbox-container {
display: block;
position: relative;
margin-bottom: 5px;
margin-bottom: 8px;
cursor: pointer;
user-select: none;
padding-right: 22px;
/* padding: 12px 40px 12px 0; */
border-radius: var(--shape-md);
}
& input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
&:checked ~ .checkbox:after {
display: block;
}
}
.checkbox-container input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
&:hover input ~ .checkbox {
border-color: var(--accent);
}
.checkbox-container input:checked ~ .checkbox {
background-color: var(--tp-primary);
border-color: var(--tp-primary);
}
&:active input ~ .checkbox {
border-color: var(--accent_light);
}
.checkbox-container input:checked ~ .checkbox:after {
display: block;
}
& .checkbox:after {
left: 2px;
bottom: 0;
font-size: 13px;
font-family: fontello;
content: "\e811";
}
.checkbox-container input:focus ~ .checkbox {
box-shadow: 0 0 0 3px var(--tp-primary-container);
}
.checkbox-container:hover input ~ .checkbox {
border-color: var(--tp-on-surface-variant);
}
.checkbox-container:active input ~ .checkbox {
border-color: var(--tp-primary);
}
.checkbox-container .checkbox:after {
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-size: 12px;
font-family: fontello;
content: "\e811";
color: var(--tp-on-primary);
}
.pref-group {
display: inline;
}
.preferences {
& button {
margin: 6px 0 3px 0;
.preferences-panel {
max-width: 600px;
width: 100%;
margin: 0 auto;
margin-top: 10px;
background-color: var(--tp-surface-container-low);
border-radius: var(--shape-xl);
padding: 0;
overflow: hidden;
}
.preferences-header {
padding: 24px 24px 16px;
color: var(--tp-on-primary-container);
margin-bottom: 0;
}
.preferences-header h2 {
font-size: 24px;
font-weight: 600;
margin: 0 0 4px 0;
color: var(--tp-on-primary-container);
}
.preferences-header p {
margin: 0;
font-size: 14px;
opacity: 0.9;
}
.preferences-close {
position: absolute;
top: 16px;
right: 16px;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--shape-sm);
color: var(--tp-on-primary-container);
transition: background 0.15s ease;
text-decoration: none;
}
.preferences-close:hover {
background-color: rgba(0, 0, 0, 0.1);
text-decoration: none;
}
.preferences-close .icon-close:before {
font-family: fontello;
content: "\e80a";
font-size: 18px;
}
.preferences-form {
padding: 16px 24px;
}
.preferences-form fieldset {
border: 0;
padding: 0;
margin: 0;
}
.pref-section {
margin-bottom: 24px;
}
.pref-section legend {
width: 100%;
padding: 0 0 12px 0;
border: 0;
font-size: 14px;
font-weight: 600;
color: var(--tp-primary);
margin-bottom: 12px;
border-bottom: 1px solid var(--tp-outline-variant);
}
.pref-grid {
display: flex;
flex-direction: column;
gap: 4px;
}
.pref-grid .pref-group {
/* background: var(--tp-surface-container); */
border-radius: var(--shape-md);
margin: 0;
transition: background-color 0.15s ease;
}
.pref-grid .pref-group:hover {
background: var(--tp-surface-container-high);
}
.pref-input {
display: flex;
flex-direction: column;
gap: 8px;
padding: 12px 0;
}
.pref-input label {
padding-right: 0;
font-weight: 500;
color: var(--tp-on-surface);
}
.prefs-code-wrapper {
position: relative;
margin: 12px 0;
}
.prefs-code-wrapper .prefs-code {
background: var(--tp-surface-container-highest);
border-radius: var(--shape-md);
padding: 16px 48px 16px 16px;
margin: 0;
font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, monospace;
font-size: 13px;
color: var(--tp-on-surface);
word-break: break-all;
overflow-x: auto;
}
.prefs-code-wrapper .prefs-code code {
font-family: inherit;
}
.copy-prefs-btn {
position: absolute;
top: 50%;
right: 8px;
transform: translateY(-50%);
width: 36px;
height: 36px;
border-radius: var(--shape-md);
background: var(--tp-surface-container);
border: 0;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.15s ease;
padding: 0;
}
.copy-prefs-btn:hover {
background: var(--tp-primary-container);
}
.copy-prefs-btn .icon-copy:before {
font-family: fontello;
content: "\e806";
color: var(--tp-on-surface-variant);
}
.preferences-actions {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 16px 24px;
border-top: 1px solid var(--tp-outline-variant);
margin-top: 24px;
}
.preferences-actions .pref-submit {
background: var(--tp-primary);
color: var(--tp-on-primary);
font-weight: 500;
padding: 12px 24px;
border-radius: var(--shape-md);
}
.preferences-actions .pref-submit:hover {
background: var(--tp-on-primary-container);
}
.pref-reset-form {
padding: 0;
margin: 0;
}
.pref-reset-form button,
.preferences-actions .pref-reset {
background: transparent;
color: var(--tp-error);
border: 1px solid var(--tp-error);
box-shadow: none;
}
.pref-reset-form button:hover,
.preferences-actions .pref-reset:hover {
background: var(--tp-error-container);
color: var(--tp-on-error-container);
}
.preferences-footer-note {
margin: 0 24px 24px;
padding: 12px 16px;
background: var(--tp-surface-container-high);
border-radius: var(--shape-md);
font-size: 13px;
color: var(--tp-on-surface-variant);
text-align: center;
border: 0;
}
.bookmark-note {
font-size: 14px;
color: var(--tp-on-surface-variant);
margin: 8px 0;
line-height: 1.5;
}
.preferences button {
/* margin: 6px 0 3px 0; */
}
.preferences label {
padding-right: 150px;
}
.preferences select {
position: absolute;
top: 0;
right: 0;
display: block;
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
}
.preferences input[type="text"],
.preferences input[type="number"] {
position: absolute;
right: 0;
max-width: 140px;
}
.preferences .pref-group {
display: block;
}
.preferences .pref-input {
position: relative;
margin-bottom: 6px;
}
/* .preferences .pref-reset { */
/* float: left; */
/* } */
/* Reduced motion preference */
@media (prefers-reduced-motion: reduce) {
button,
.checkbox,
.checkbox-container,
.preferences-close,
.pref-section,
.copy-prefs-btn,
input[type="text"],
input[type="number"],
select {
transition: none !important;
animation: none !important;
}
& label {
padding-right: 150px;
box-sizing: border-box;
max-width: 100%;
.preferences-panel {
animation: none;
}
}
& select {
position: absolute;
top: 0;
right: 0;
display: block;
padding: 0 6px;
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
/* Mobile responsive */
@media (max-width: 600px) {
.preferences-panel {
max-width: 95vw;
border-radius: var(--shape-lg);
margin-top: 8px;
}
& input[type="text"],
& input[type="number"] {
position: absolute;
right: 0;
max-width: 140px;
.preferences-header {
padding: 20px 16px 12px;
}
& .pref-group {
display: block;
.preferences-header h2 {
font-size: 20px;
}
& .pref-input {
position: relative;
margin-bottom: 6px;
.preferences-form {
padding: 12px 16px;
}
& .pref-reset {
float: left;
.pref-reset-form {
padding: 0 16px 16px;
}
.preferences-footer-note {
margin: 0 16px 16px;
}
.nav-item,
.nav-item .icon-container {
font-size: 16px;
}
}
+65 -19
View File
@@ -1,14 +1,30 @@
.material-symbols-outlined {
font-family: 'Material Symbols Outlined';
font-weight: 300;
font-style: normal;
font-size: 18px !important;
display: inline-block;
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
user-select: none;
}
nav {
display: flex;
align-items: center;
position: fixed;
background-color: var(--bg_overlays);
box-shadow: 0 0 4px var(--shadow);
background-color: var(--bg_color);
padding: 0;
width: 100%;
height: 50px;
height: 56px;
z-index: 1000;
font-size: 16px;
border-bottom: 1px solid var(--tp-outline-variant);
& a,
& .icon-button button {
@@ -19,35 +35,37 @@ nav {
.inner-nav {
margin: auto;
box-sizing: border-box;
padding: 0 10px;
padding: 0 16px;
display: flex;
align-items: center;
flex-basis: 920px;
height: 50px;
flex-basis: 600px;
height: 56px;
}
.site-name {
font-size: 15px;
font-weight: 600;
font-size: 16px;
font-weight: 500;
line-height: 1;
color: var(--tp-primary);
&:hover {
color: var(--accent_light);
text-decoration: unset;
color: var(--tp-on-primary-container);
text-decoration: none;
}
}
.site-logo {
display: block;
width: 35px;
height: 35px;
width: 32px;
height: 32px;
border-radius: var(--shape-md);
}
.nav-item {
display: flex;
flex: 1;
line-height: 50px;
height: 50px;
line-height: 56px;
height: 56px;
overflow: hidden;
flex-wrap: wrap;
align-items: center;
@@ -57,9 +75,19 @@ nav {
justify-content: flex-end;
}
&.right a:hover {
color: var(--accent_light);
text-decoration: unset;
&.right a {
color: var(--tp-on-surface-variant);
font-weight: 500;
padding: 0;
border-radius: var(--shape-sm);
margin: 0;
transition: color 0.15s ease;
&:hover {
color: var(--tp-primary);
background-color: var(--bg_hover);
text-decoration: none;
}
}
}
@@ -71,7 +99,7 @@ nav {
fill: var(--fg_nav);
&:hover {
fill: var(--accent_light);
fill: var(--tp-on-primary-container);
}
}
@@ -81,5 +109,23 @@ nav {
.icon-cog {
font-size: 15px;
padding-left: 0 !important;
/* padding-left: 0 !important; */
}
.nav-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: var(--shape-sm);
color: var(--tp-on-surface-variant);
transition: color 0.15s ease;
text-decoration: none;
}
.nav-icon:hover {
color: var(--tp-primary);
background-color: var(--bg_hover);
text-decoration: none;
}
+128 -43
View File
@@ -2,72 +2,140 @@
@import "photo-rail.css";
.profile-tabs {
max-width: 900px;
max-width: 1200px;
margin: 0 auto;
float: none;
border-radius: 0;
position: relative;
width: auto;
& .timeline-container {
float: right;
width: 68% !important;
max-width: unset;
}
display: flex;
flex-direction: row;
gap: 12px;
padding: 8px;
align-items: flex-start;
}
/* Left sidebar containing profile card and photo rail */
.profile-sidebar {
flex: 0 0 320px;
display: flex;
flex-direction: column;
gap: 16px;
}
.profile-banner {
margin-bottom: 4px;
background-color: var(--bg_panel);
width: 100%;
background-color: var(--tp-surface-container);
overflow: hidden;
}
& a {
display: block;
position: relative;
padding: 33.34% 0 0 0;
}
.profile-banner a {
display: block;
position: relative;
padding: 33.34% 0 0 0;
}
& img {
max-width: 100%;
position: absolute;
top: 0;
}
.profile-banner img {
max-width: 100%;
position: absolute;
top: 0;
}
.profile-header {
position: relative;
}
.profile-tab {
padding: 0 4px 0 0;
flex: 0 0 400px;
box-sizing: border-box;
display: inline-block;
padding: 0;
font-size: 14px;
text-align: left;
vertical-align: top;
max-width: 32%;
top: 50px;
}
.profile-tab.sticky {
position: sticky;
top: 72px;
max-height: calc(100vh - 88px);
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: var(--tp-outline-variant) transparent;
}
.profile-tab.sticky::-webkit-scrollbar {
width: 6px;
}
.profile-tab.sticky::-webkit-scrollbar-track {
background: transparent;
}
.profile-tab.sticky::-webkit-scrollbar-thumb {
background: var(--tp-outline-variant);
border-radius: 3px;
}
/* Timeline container - main content area */
.timeline-container {
flex: 1;
min-width: 0;
max-width: 600px;
}
.profile-result {
min-height: 54px;
}
& .username {
margin: 0 !important;
.profile-result .username {
margin: 0 !important;
}
.profile-result .tweet-header {
margin-bottom: unset;
}
/* Responsive layout */
@media (max-width: 900px) {
.profile-tabs {
flex-direction: column;
padding: 8px;
gap: 16px;
}
& .tweet-header {
margin-bottom: unset;
.profile-sidebar,
.profile-tab {
flex: 1;
width: 100%;
max-width: unset;
}
.profile-tab.sticky {
position: relative;
top: unset;
max-height: unset;
overflow-y: visible;
}
.timeline-container {
max-width: unset;
width: 100%;
}
.profile-banner {
margin: 0;
}
}
@media (max-width: 700px) {
.profile-tabs {
width: 100vw;
width: 100%;
max-width: 600px;
padding: 0 8px;
}
& .timeline-container {
width: 100% !important;
.timeline-container {
width: 100%;
}
& .tab-item wide {
flex-grow: 1.4;
}
}
.timeline-container .tab-item wide {
flex-grow: 1.4;
}
.profile-tab {
@@ -76,10 +144,27 @@
position: initial !important;
padding: 0;
}
}
@media (min-height: 900px) {
.profile-tab.sticky {
position: sticky;
.profile-banner {
border-radius: 0;
/* margin-left: -8px; */
/* margin-right: -8px; */
}
}
@media (min-height: 900px) and (min-width: 900px) {
.profile-tab.sticky {
position: sticky;
border-radius: 10px;
}
}
/* Reduced motion preference */
@media (prefers-reduced-motion: reduce) {
.profile-tabs,
.profile-sidebar > *,
.timeline-container {
animation: none;
}
}
+154 -77
View File
@@ -1,134 +1,211 @@
.profile-card {
flex-wrap: wrap;
background: var(--bg_panel);
padding: 12px;
background: var(--tp-surface-container);
padding: 10px;
padding-top: 62px;
display: flex;
flex-direction: column;
position: relative;
/* border-radius: var(--shape-lg); */
/* transition: box-shadow 0.2s ease, transform 0.2s ease; */
}
.profile-card-info {
overflow: hidden;
/* .profile-card:hover {
box-shadow: var(--elevation-2);
}
*/
.profile-card-identity {
overflow-wrap: break-word;
width: 100%;
}
.profile-card-tabs-name {
overflow: hidden;
overflow-wrap: break-word;
max-width: 100%;
margin-bottom: 8px;
}
.profile-card-username {
overflow: hidden;
overflow-wrap: break-word;
color: var(--fg_color);
font-size: 14px;
color: var(--tp-accent-blue);
font-size: 15px;
/* font-weight: 400; */
display: block;
/* margin-top: 4px; */
transition: color 0.15s ease;
}
.profile-card-username:hover {
color: var(--tp-primary);
}
.profile-card-fullname {
overflow: hidden;
overflow-wrap: break-word;
color: var(--fg_color);
font-size: 16px;
font-weight: bold;
color: var(--tp-on-surface);
font-size: 20px;
font-weight: 500;
letter-spacing: -0.1px;
font-family: "Readex Pro", sans-serif;
text-shadow: none;
max-width: 100%;
transition: color 0.15s ease;
}
.profile-card-fullname:hover {
color: var(--tp-primary);
text-decoration: none;
}
.profile-card-avatar {
display: inline-block;
position: relative;
width: 100%;
margin-right: 4px;
margin-bottom: 6px;
position: absolute;
top: -50px;
left: 16px;
width: 100px;
height: 100px;
display: block;
z-index: 1;
}
&:after {
content: '';
display: block;
margin-top: 100%;
}
& img {
box-sizing: border-box;
position: absolute;
width: 100%;
height: 100%;
border: 4px solid var(--darker_grey);
background: var(--bg_panel);
}
.profile-card-avatar img {
box-sizing: border-box;
position: static;
width: 100px;
height: 100px;
display: block;
background: var(--tp-surface-container);
border-radius: 50%;
border: 4px solid var(--tp-surface-container)
}
.profile-card-extra {
display: contents;
flex: 100%;
margin-top: 7px;
margin-top: 12px;
}
& .profile-bio {
overflow: hidden;
overflow-wrap: break-word;
width: 100%;
margin: 4px -6px 6px 0;
white-space: pre-wrap;
.profile-card-extra .profile-bio {
overflow: hidden;
overflow-wrap: break-word;
width: 100%;
margin: 8px 0 12px 0;
white-space: pre-wrap;
font-size: 14px;
line-height: 1.5;
color: var(--tp-on-surface);
}
& p {
margin: 0;
}
}
.profile-card-extra .profile-bio p {
margin: 0;
}
& .profile-joindate,
& .profile-location,
& .profile-website {
color: var(--fg_faded);
margin: 1px 0;
width: 100%;
}
.profile-card-extra .profile-joindate,
.profile-card-extra .profile-location,
.profile-card-extra .profile-website {
color: var(--tp-on-surface-variant);
margin: 2px 0;
width: 100%;
font-size: 14px;
display: flex;
align-items: center;
gap: 6px;
}
.profile-card-extra .profile-joindate::before,
.profile-card-extra .profile-location::before,
.profile-card-extra .profile-website::before {
font-family: fontello;
opacity: 0.7;
}
.profile-card-extra .profile-joindate a,
.profile-card-extra .profile-location a,
.profile-card-extra .profile-website a {
color: var(--tp-primary);
transition: color 0.15s ease;
}
.profile-card-extra .profile-joindate a:hover,
.profile-card-extra .profile-location a:hover,
.profile-card-extra .profile-website a:hover {
color: var(--tp-on-primary-container);
}
.profile-card-extra-links {
margin-top: 8px;
margin-top: 10px;
font-size: 14px;
width: 100%;
/* padding-top: 12px; */
}
.profile-statlist {
display: flex;
flex-wrap: wrap;
flex-wrap: nowrap;
padding: 0;
width: 100%;
justify-content: space-between;
& li {
display: table-cell;
text-align: center;
}
gap: 2px;
list-style: none;
margin: 0;
}
.profile-statlist li {
/* display: flex;
flex-direction: column;
align-items: center; */
padding: 10px;
border-radius: var(--shape-sm);
transition: background-color 0.15s ease;
flex: 1;
min-width: 60px;
cursor: pointer;
}
/* .profile-statlist li:hover {
background-color: var(--tp-surface-container-high);
} */
.profile-stat-header {
font-weight: bold;
color: var(--profile_stat);
font-size: 14px;
color: var(--tp-on-surface-variant);
font-weight: 400;
/* text-transform: uppercase; */
/* letter-spacing: 0.5px; */
margin-bottom: 4px;
}
.profile-stat-num {
display: block;
color: var(--profile_stat);
color: var(--tp-on-surface);
font-size: 16px;
font-weight: 600;
transition: color 0.15s ease;
}
.profile-statlist li:hover .profile-stat-num {
color: var(--tp-primary);
}
/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
.profile-card,
.profile-card-avatar img,
.profile-statlist li {
transition: none;
}
}
@media (max-width: 700px) {
.profile-card-info {
display: flex;
}
.profile-card-tabs-name {
flex-shrink: 100;
}
.profile-card-avatar {
width: 80px;
height: 80px;
width: 120px;
height: 120px;
top: -60px;
}
& img {
border-width: 2px;
width: unset;
}
.profile-card-avatar img {
width: 120px;
height: 120px;
}
.profile-card {
padding-top: 70px;
margin: 0;
}
}
+157 -72
View File
@@ -1,75 +1,154 @@
.photo-rail {
&-card {
float: left;
background: var(--bg_panel);
border-radius: 0 0 4px 4px;
width: 100%;
margin: 5px 0;
}
&-header {
padding: 5px 12px 0;
}
&-header-mobile {
display: none;
box-sizing: border-box;
padding: 5px 12px 0;
width: 100%;
float: unset;
color: var(--accent);
justify-content: space-between;
}
&-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-gap: 3px 3px;
padding: 5px 12px 12px;
& a {
position: relative;
border-radius: 5px;
background-color: var(--darker_grey);
&:before {
content: "";
display: block;
padding-top: 100%;
}
}
& img {
height: 100%;
width: 100%;
border-radius: 4px;
object-fit: cover;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
}
.photo-rail-card {
background: var(--tp-surface-container);
border-bottom-left-radius: var(--shape-md);
border-bottom-right-radius: var(--shape-md);
width: 100%;
/* box-shadow: var(--elevation-1); */ overflow: hidden;
transition: box-shadow 0.2s ease;
}
/* create-toggle(photo-rail-grid, 640px) */
.photo-rail-card:hover {
/* box-shadow: var(--elevation-2); */
}
.photo-rail-header {
padding: 12px 16px;
color: var(--tp-on-surface);
display: flex;
align-items: center;
justify-content: space-between;
}
.photo-rail-header a {
display: flex;
align-items: center;
gap: 10px;
color: var(--tp-on-surface);
transition: color 0.15s ease;
text-decoration: none;
position: relative;
}
.photo-rail-header a:hover {
color: var(--tp-primary);
text-decoration: none;
}
.photo-rail-icon-wrap {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
/* width: 24px;
height: 24px; */
}
.photo-rail-media-badge {
position: absolute;
top: -12px;
right: -10px;
display: inline-flex;
align-items: center;
justify-content: center;
background-color: var(--tp-accent-red);
color: var(--tp-surface);
font-size: 10px;
font-weight: 500;
min-width: 18px;
height: 18px;
border-radius: var(--shape-full);
padding: 0 4px;
line-height: 1;
box-sizing: border-box;
border: 2px solid var(--tp-surface-container);
}
.photo-rail-media-icon {
color: var(--tp-on-surface-variant);
font-size: 20px !important;
position: relative;
}
.photo-rail-media-label {
font-size: 14px;
color: var(--tp-on-surface-variant);
font-weight: 400;
}
.photo-rail-header-mobile-inner {
display: flex;
align-items: center;
gap: 10px;
}
.photo-rail-header-mobile {
display: none;
box-sizing: border-box;
padding: 16px;
width: 100%;
color: var(--tp-primary);
justify-content: space-between;
align-items: center;
font-weight: 600;
font-size: 16px;
cursor: pointer;
/* border-radius: var(--shape-lg); */
transition: background-color 0.15s ease;
}
.photo-rail-header-mobile:hover {
background-color: var(--tp-surface-container-high);
}
.photo-rail-grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 4px;
padding: 4px;
background: var(--tp-surface-container);
}
.photo-rail-grid a {
position: relative;
display: block;
border-radius: var(--shape-sm);
background-color: var(--tp-surface-container-highest);
overflow: hidden;
aspect-ratio: 1;
min-width: 0;
min-height: 0;
}
.photo-rail-grid img {
display: block;
height: 100%;
width: 100%;
object-fit: contain;
position: absolute;
top: 0;
left: 0;
}
/* Mobile toggle */
#photo-rail-grid-toggle {
display: none;
&:checked ~ .photo-rail-grid {
max-height: 640px;
}
&:checked ~ label .icon-down:before {
transform: rotate(180deg) translateY(-1px);
}
}
#photo-rail-grid-toggle:checked ~ .photo-rail-grid {
padding-bottom: 12px;
max-height: 640px;
padding: 8px;
}
#photo-rail-grid-toggle:checked ~ label .icon-down:before {
transform: rotate(180deg) translateY(-1px);
}
#photo-rail-grid-toggle:checked ~ .photo-rail-grid {
padding-bottom: 8px;
}
@media (max-width: 700px) {
.photo-rail-header {
display: none;
@@ -81,26 +160,32 @@
.photo-rail-grid {
max-height: 0;
padding-bottom: 0;
overflow: hidden;
transition: max-height 0.4s;
}
.photo-rail-grid {
grid-template-columns: repeat(6, 1fr);
transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1);
grid-template-columns: repeat(5, 1fr);
gap: 4px;
padding: 0;
}
#photo-rail-grid-toggle:checked ~ .photo-rail-grid {
max-height: 300px;
max-height: 400px;
padding: 8px;
}
.photo-rail-card {
border-bottom-left-radius: var(--shape-md);
border-bottom-right-radius: var(--shape-md);
margin: 0;
}
}
@media (max-width: 450px) {
.photo-rail-grid {
grid-template-columns: repeat(4, 1fr);
grid-template-columns: repeat(5, 1fr);
}
#photo-rail-grid-toggle:checked ~ .photo-rail-grid {
max-height: 450px;
max-height: 320px;
}
}
+170 -116
View File
@@ -1,117 +1,173 @@
.search-title {
font-weight: bold;
font-weight: 600;
display: inline-block;
margin-top: 4px;
color: var(--tp-on-surface);
}
.search-field {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center; /* add this */
}
& button {
margin: 0 2px 0 0;
padding: 0px 1px 1px 4px;
height: 23px;
display: flex;
align-items: center;
}
.search-field button {
margin: 0;
padding: 8px 16px;
height: 36px;
/* display: flex; */
background-color: var(--tp-primary);
color: var(--tp-on-primary);
border: none;
border-radius: var(--shape-md);
font-weight: 500;
cursor: pointer;
/* transition: all 0.15s ease;
box-shadow: var(--elevation-1); */
}
& .pref-input {
margin: 0 4px 0 0;
flex-grow: 1;
height: 23px;
}
.search-field button:hover {
background-color: var(--tp-on-primary-container);
}
& input[type="text"],
& input[type="number"] {
height: calc(100% - 4px);
width: calc(100% - 8px);
}
.search-field .pref-input {
margin: 0;
flex-grow: 1;
height: auto;
}
& > label {
display: inline;
background-color: var(--bg_elements);
color: var(--fg_color);
border: 1px solid var(--accent_border);
padding: 1px 1px 2px 4px;
font-size: 14px;
cursor: pointer;
margin-bottom: 2px;
.search-field input[type="text"],
.search-field input[type="number"] {
height: 36px;
width: 100%;
background-color: var(--tp-surface-container-highest);
border: 1px solid transparent;
padding: 0 12px;
color: var(--tp-on-surface);
font-size: 14px;
}
&:hover {
border-color: var(--accent);
}
.search-field input[type="text"]:hover,
.search-field input[type="number"]:hover {
background-color: var(--tp-surface-container-high);
}
&:active {
border-color: var(--accent_light);
}
}
.search-field input[type="text"]:focus,
.search-field input[type="number"]:focus {
background-color: var(--tp-surface-container-highest);
border-color: var(--tp-primary);
outline: none;
}
/* create-toggle(search-panel, 380px) */
& #search-panel-toggle {
display: none;
.search-field > label {
display: inline-flex;
align-items: center;
/* background-color: var(--tp-surface-container); */
color: var(--tp-on-surface-variant);
border: 1px solid var(--tp-outline-variant);
padding: 4px 4px;
font-size: 14px;
cursor: pointer;
border-radius: var(--shape-md);
font-weight: 500;
transition: all 0.15s ease;
}
&:checked ~ .search-panel {
max-height: 380px;
}
.search-field > label:hover {
border-color: var(--tp-outline);
background-color: var(--tp-surface-container-high);
}
&:checked ~ label .icon-down:before {
transform: rotate(180deg) translateY(-1px);
}
}
.search-field > label:active {
background-color: var(--tp-surface-container-highest);
}
/* create-toggle(search-panel, 380px) */
.search-field #search-panel-toggle {
display: none;
}
.search-field #search-panel-toggle:checked ~ .search-panel {
max-height: 380px;
}
.search-field #search-panel-toggle:checked ~ label .icon-down:before {
transform: rotate(180deg) translateY(-1px);
}
.search-panel {
width: 100%;
max-height: 0;
overflow: hidden;
transition: max-height 0.4s;
transition: max-height 0.4s ease;
flex-grow: 1;
font-weight: initial;
text-align: left;
& .checkbox-container {
display: inline;
padding-right: unset;
margin-bottom: 5px;
margin-left: 23px;
}
& .checkbox {
right: unset;
left: -22px;
line-height: 1.6em;
}
& .checkbox-container .checkbox:after {
top: -4px;
}
/* text-align: left; */
background-color: var(--tp-surface-container);
border-radius: var(--shape-lg);
margin-top: 8px;
}
.search-panel .checkbox-container {
display: inline;
padding-right: unset;
margin-bottom: 5px;
margin-left: 23px;
}
.search-panel .checkbox {
right: unset;
left: -22px;
line-height: 1.6em;
background-color: var(--tp-surface-container-highest);
border: 2px solid var(--tp-outline);
border-radius: var(--shape-xs);
}
.search-row {
display: flex;
flex-wrap: wrap;
line-height: unset;
padding: 16px;
gap: 12px;
}
& > div {
flex-grow: 1;
flex-shrink: 1;
}
.search-row > div {
flex-grow: 1;
flex-shrink: 1;
}
& input {
height: 21px;
}
.search-row input {
height: 36px;
background-color: var(--tp-surface-container-highest);
border: 1px solid transparent;
border-radius: var(--shape-sm);
padding: 0 12px;
color: var(--tp-on-surface);
font-size: 14px;
transition: all 0.15s ease;
}
& .pref-input {
display: block;
padding-bottom: 5px;
.search-row input:hover {
background-color: var(--tp-surface-container-high);
}
& input {
height: 21px;
margin-top: 1px;
}
}
.search-row input:focus {
background-color: var(--tp-surface-container-highest);
border-color: var(--tp-primary);
outline: none;
}
.search-row .pref-input {
display: block;
padding-bottom: 5px;
}
.search-row .pref-input input {
height: 36px;
margin-top: 1px;
}
.search-toggles {
@@ -122,55 +178,53 @@
}
/* search-resize expansions for .profile-tabs context */
.profile-tabs {
@media (max-width: 820px) {
& .search-toggles {
grid-template-columns: repeat(5, auto);
}
& #search-panel-toggle:checked ~ .search-panel {
max-height: 380px !important;
}
@media (max-width: 820px) {
.profile-tabs .search-toggles {
grid-template-columns: repeat(5, auto);
}
@media (max-width: 715px) {
& .search-toggles {
grid-template-columns: repeat(4, auto);
}
.profile-tabs #search-panel-toggle:checked ~ .search-panel {
max-height: 380px !important;
}
}
& #search-panel-toggle:checked ~ .search-panel {
max-height: 380px !important;
}
@media (max-width: 715px) {
.profile-tabs .search-toggles {
grid-template-columns: repeat(4, auto);
}
@media (max-width: 700px) {
& .search-toggles {
grid-template-columns: repeat(5, auto);
}
.profile-tabs #search-panel-toggle:checked ~ .search-panel {
max-height: 380px !important;
}
}
& #search-panel-toggle:checked ~ .search-panel {
max-height: 380px !important;
}
@media (max-width: 700px) {
.profile-tabs .search-toggles {
grid-template-columns: repeat(5, auto);
}
@media (max-width: 485px) {
& .search-toggles {
grid-template-columns: repeat(4, auto);
}
.profile-tabs #search-panel-toggle:checked ~ .search-panel {
max-height: 380px !important;
}
}
& #search-panel-toggle:checked ~ .search-panel {
max-height: 380px !important;
}
@media (max-width: 485px) {
.profile-tabs .search-toggles {
grid-template-columns: repeat(4, auto);
}
@media (max-width: 410px) {
& .search-toggles {
grid-template-columns: repeat(3, auto);
}
.profile-tabs #search-panel-toggle:checked ~ .search-panel {
max-height: 380px !important;
}
}
& #search-panel-toggle:checked ~ .search-panel {
max-height: 380px !important;
}
@media (max-width: 410px) {
.profile-tabs .search-toggles {
grid-template-columns: repeat(3, auto);
}
.profile-tabs #search-panel-toggle:checked ~ .search-panel {
max-height: 380px !important;
}
}
+266 -248
View File
@@ -5,61 +5,112 @@
@import "inputs.css";
@import "timeline.css";
@import "search.css";
@import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0&display=block');
@import url('https://fonts.googleapis.com/css2?family=Readex+Pro:[email protected]&display=swap');
body {
--bg_color: #0f0f0f;
--fg_color: #f8f8f2;
--fg_faded: #f8f8f2cf;
--fg_dark: #ff6c60;
--fg_nav: #ff6c60;
--bg_color: #121214;
--fg_color: #A0A0AF;
--fg_faded: #606069;
--fg_dark: #A0A0AF;
--fg_nav: #A0A0AF;
--bg_panel: #161616;
--bg_elements: #121212;
--bg_overlays: #1f1f1f;
--bg_hover: #1a1a1a;
--bg_panel: #171719;
--bg_elements: #1F1F21;
--bg_overlays: #252527;
--bg_hover: #252527;
--grey: #888889;
--dark_grey: #404040;
--darker_grey: #282828;
--darkest_grey: #222222;
--border_grey: #3e3e35;
--grey: #606069;
/* --dark_grey: #2A2A2C; */
--darker_grey: #2A2A2C;
--darkest_grey:#2A2A2C;
--border_grey: #3A3A3C;
--accent: #ff6c60;
--accent_light: #ffaca0;
--accent_dark: #8a3731;
--accent_border: #ff6c6091;
--accent: #A0A0AF;
--accent_light: #B8B8C7;
--accent_dark: #888899;
--accent_border: #2A2A2C;
--play_button: #d8574d;
--play_button_hover: #ff6c60;
--play_button: #A0A0AF;
--play_button_hover: #B8B8C7;
--more_replies_dots: #A0A0AF;
--error_red: #2A2028;
--more_replies_dots: #ad433b;
--error_red: #420a05;
--verified_blue: #6C81B8;
--verified_business: #A68170;
--verified_government: #8a8a8a;
--icon_text: #171719;
--verified_blue: #1da1f2;
--verified_business: #fac82b;
--verified_government: #c1b6a4;
--icon_text: #f8f8f2;
--tab: #606069;
--tab_selected: #A0A0AF;
--profile_stat: #A0A0AF;
--tab: #f8f8f2;
--tab_selected: #ff6c60;
--shadow: rgba(0, 0, 0, 0.24);
--shadow_dark: rgba(0, 0, 0, 0.36);
--profile_stat: #f8f8f2;
--tp-primary: var(--accent);
--tp-on-primary: var(--icon_text);
--tp-primary-container: var(--accent_border);
--tp-on-primary-container: var(--fg_nav);
--shadow: rgba(0, 0, 0, 0.6);
--shadow_dark: rgba(0, 0, 0, 0.2);
--tp-secondary: var(--accent_dark);
--tp-on-secondary: var(--icon_text);
--tp-secondary-container: var(--dark_grey);
--tp-on-secondary-container: var(--fg_faded);
--tp-surface: var(--bg_color);
--tp-surface-dim: var(--bg_panel);
--tp-surface-container-lowest: var(--bg_color);
--tp-surface-container-low: var(--bg_panel);
--tp-surface-container: var(--bg_elements);
--tp-surface-container-high: var(--bg_overlays);
--tp-surface-container-highest: var(--darkest_grey);
--tp-on-surface: var(--fg_color);
--tp-on-surface-variant: var(--fg_faded);
--tp-outline: var(--border_grey);
--tp-outline-variant: var(--dark_grey);
--tp-error: #A67E97;
--tp-error-container: var(--error_red);
--tp-on-error-container:var(--fg_color);
--tp-accent-red: #A67E97;
--tp-accent-green: #738980;
--tp-accent-yellow: var(--verified_business);
--tp-accent-blue: var(--verified_blue);
--tp-accent-magenta: #6E6DA7;
--tp-accent-cyan: #55787D;
--shape-none: 0px;
--shape-xs: 2px;
--shape-sm: 3px;
--shape-md: 4px;
--shape-lg: 6px;
--shape-xl: 8px;
--shape-full: 9999px;
--elevation-0: none;
--elevation-1: 0 1px 2px rgba(0, 0, 0, 0.24), 0 1px 3px rgba(0, 0, 0, 0.12);
--elevation-2: 0 2px 4px rgba(0, 0, 0, 0.24), 0 2px 6px rgba(0, 0, 0, 0.16);
--elevation-3: 0 4px 8px rgba(0, 0, 0, 0.32), 0 4px 12px rgba(0, 0, 0, 0.20);
background-color: var(--bg_color);
color: var(--fg_color);
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, fontello;
font-size: 15px;
line-height: 1.3;
--font-family: 'Readex Pro', sans-serif, Twemoji;
font-family: var(--font-family);
font-size: 14px;
line-height: 1.5;
margin: 0;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
* {
outline: unset;
margin: 0;
text-decoration: none;
font-weight: 300;
}
h1 {
@@ -77,10 +128,10 @@ p {
a {
color: var(--accent);
}
&:hover {
text-decoration: underline;
}
a:hover {
text-decoration: underline;
}
fieldset {
@@ -100,11 +151,6 @@ legend {
margin-bottom: 8px;
}
.prefs-code {
white-space: pre-wrap;
word-break: break-all;
}
.preferences .note {
border-top: 1px solid var(--border_grey);
border-bottom: 1px solid var(--border_grey);
@@ -117,192 +163,192 @@ legend {
max-width: 700px;
margin: 0 auto;
padding: 20px;
}
& .cover {
width: 100%;
max-height: 400px;
object-fit: cover;
border-radius: 8px;
margin-bottom: 20px;
}
.note .cover {
width: 100%;
max-height: 400px;
object-fit: cover;
border-radius: 8px;
margin-bottom: 20px;
}
& article {
background-color: var(--bg_panel);
padding: 20px;
border-radius: 8px;
.note article {
background-color: var(--bg_panel);
padding: 20px;
border-radius: 8px;
}
& h1 {
display: block;
font-size: 28px;
margin-bottom: 16px;
line-height: 1.3;
}
.note article h1 {
display: block;
font-size: 28px;
margin-bottom: 16px;
line-height: 1.3;
}
& h2 {
font-size: 22px;
font-weight: bold;
margin: 20px 0 10px;
}
.note article h2 {
font-size: 22px;
font-weight: bold;
margin: 20px 0 10px;
}
& h3 {
font-size: 18px;
font-weight: bold;
margin: 16px 0 8px;
}
.note article h3 {
font-size: 18px;
font-weight: bold;
margin: 16px 0 8px;
}
& p {
margin: 12px 0;
line-height: 1.6;
white-space: pre-wrap;
}
.note article p {
margin: 12px 0;
line-height: 1.6;
white-space: pre-wrap;
}
& ul,
& ol {
margin: 12px 0;
padding-left: 2em;
}
.note article ul,
.note article ol {
margin: 12px 0;
padding-left: 2em;
}
& ol {
list-style-type: decimal;
}
.note article ol {
list-style-type: decimal;
}
& li {
margin: 6px 0;
padding-left: 0.3em;
line-height: 1.6;
}
.note article li {
margin: 6px 0;
padding-left: 0.3em;
line-height: 1.6;
}
& .code-block {
position: relative;
margin: 16px 0;
border-radius: 6px;
background-color: var(--bg_color);
overflow: hidden;
.note article .code-block {
position: relative;
margin: 16px 0;
border-radius: 6px;
background-color: var(--bg_color);
overflow: hidden;
}
& .code-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 4px 10px;
background-color: var(--darker_grey);
font-size: 12px;
color: var(--fg_faded);
min-height: 24px;
.note article .code-block .code-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 4px 10px;
background-color: var(--darker_grey);
font-size: 12px;
color: var(--fg_faded);
min-height: 24px;
}
& .code-lang {
font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, "DejaVu Sans Mono", monospace;
}
.note article .code-block .code-header .code-lang {
font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, "DejaVu Sans Mono", monospace;
}
& .copy-btn {
background: none;
border: none;
color: var(--fg_faded);
cursor: pointer;
padding: 2px;
border-radius: 3px;
display: flex;
align-items: center;
.note article .code-block .code-header .copy-btn {
background: none;
border: none;
color: var(--fg_faded);
cursor: pointer;
padding: 2px;
border-radius: 3px;
display: flex;
align-items: center;
}
&:hover {
color: var(--fg_color);
}
}
}
.note article .code-block .code-header .copy-btn:hover {
color: var(--fg_color);
}
& .copy-toast {
position: absolute;
top: 4px;
right: 36px;
background-color: var(--bg_elements);
color: var(--fg_color);
font-size: 12px;
padding: 4px 8px;
border-radius: 4px;
pointer-events: none;
animation: toast-fade 1.5s ease forwards;
}
.note article .code-block .copy-toast {
position: absolute;
top: 4px;
right: 36px;
background-color: var(--bg_elements);
color: var(--fg_color);
font-size: 12px;
padding: 4px 8px;
border-radius: 4px;
pointer-events: none;
animation: toast-fade 1.5s ease forwards;
}
& pre {
margin: 0;
padding: 16px;
overflow-x: auto;
.note article .code-block pre {
margin: 0;
padding: 16px;
overflow-x: auto;
}
& code {
font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, "DejaVu Sans Mono", monospace;
font-size: 14px;
line-height: 1.5;
white-space: pre;
}
}
}
.note article .code-block pre code {
font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, "DejaVu Sans Mono", monospace;
font-size: 14px;
line-height: 1.5;
white-space: pre;
}
& pre {
margin: 16px 0;
padding: 16px;
background-color: var(--bg_color);
border-radius: 6px;
overflow-x: auto;
.note article pre {
margin: 16px 0;
padding: 16px;
background-color: var(--bg_color);
border-radius: 6px;
overflow-x: auto;
}
& code {
font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, "DejaVu Sans Mono", monospace;
font-size: 14px;
line-height: 1.5;
white-space: pre;
}
}
.note article pre code {
font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, "DejaVu Sans Mono", monospace;
font-size: 14px;
line-height: 1.5;
white-space: pre;
}
& blockquote {
margin: 12px 0;
padding: 4px 0 4px 16px;
border-left: 3px solid var(--accent);
color: var(--fg_faded);
line-height: 1.6;
}
}
.note article blockquote {
margin: 12px 0;
padding: 4px 0 4px 16px;
border-left: 3px solid var(--accent);
color: var(--fg_faded);
line-height: 1.6;
}
& .author {
display: flex;
align-items: center;
margin-bottom: 20px;
color: var(--fg_faded);
font-size: 14px;
.note .author {
display: flex;
align-items: center;
margin-bottom: 20px;
color: var(--fg_faded);
font-size: 14px;
}
& .fullname {
font-weight: bold;
color: var(--fg_color);
}
.note .author .fullname {
font-weight: bold;
color: var(--fg_color);
}
& .username {
color: var(--fg_dark);
}
}
.note .author .username {
color: var(--fg_dark);
}
& .image {
display: flex;
justify-content: center;
margin: 16px 0;
.note .image {
display: flex;
justify-content: center;
margin: 16px 0;
}
& img {
max-width: 100%;
border-radius: 6px;
}
.note .image img {
max-width: 100%;
border-radius: 6px;
}
& video {
max-width: 100%;
border-radius: 6px;
}
}
.note .image video {
max-width: 100%;
border-radius: 6px;
}
& .twemoji {
height: 1.2em;
vertical-align: middle;
}
.note .twemoji {
height: 1.2em;
vertical-align: middle;
}
& .timeline-item {
margin: 16px 0;
border-radius: 8px;
overflow: hidden;
}
.note .timeline-item {
margin: 16px 0;
border-radius: 8px;
overflow: hidden;
}
@keyframes toast-fade {
@@ -336,64 +382,36 @@ ul {
background-color: var(--bg_overlays);
padding: 10px 15px;
align-self: start;
box-sizing: border-box;
overflow: hidden;
}
& ul {
margin-bottom: 14px;
}
.overlay-panel ul {
margin-bottom: 14px;
}
& p {
word-break: break-word;
}
.overlay-panel p {
word-break: break-word;
}
.verified-icon {
display: inline-block;
width: 14px;
height: 14px;
display: inline-flex;
align-items: center;
margin-left: 2px;
}
& .verified-icon-circle {
position: absolute;
font-size: 15px;
}
.verified-icon .verified-badge-check {
fill: #ffffff;
}
& .verified-icon-check {
position: absolute;
font-size: 9px;
margin: 5px 3px;
}
.verified-icon.blue .verified-badge-bg {
fill: var(--verified_blue);
}
&.blue {
& .verified-icon-circle {
color: var(--verified_blue);
}
.verified-icon.business .verified-badge-bg {
fill: var(--verified_business);
}
& .verified-icon-check {
color: var(--icon_text);
}
}
&.business {
& .verified-icon-circle {
color: var(--verified_business);
}
& .verified-icon-check {
color: var(--bg_panel);
}
}
&.government {
& .verified-icon-circle {
color: var(--verified_government);
}
& .verified-icon-check {
color: var(--bg_panel);
}
}
.verified-icon.government .verified-badge-bg {
fill: var(--verified_government);
}
@media (max-width: 600px) {
+95
View File
@@ -0,0 +1,95 @@
/*
* Teapot Light - Light Theme for Teapot
* Warm Coral palette
*/
body {
--tp-primary: #9c4238;
--tp-on-primary: #ffffff;
--tp-primary-container: #ffdad4;
--tp-on-primary-container: #3a0a06;
--tp-secondary: #4a635e;
--tp-on-secondary: #ffffff;
--tp-secondary-container: #cce8e2;
--tp-on-secondary-container: #051f1b;
--tp-surface: #fff8f6;
--tp-surface-dim: #ebd5d1;
--tp-surface-container-lowest: #ffffff;
--tp-surface-container-low: #fff0ed;
--tp-surface-container: #ffe9e5;
--tp-surface-container-high: #fce3de;
--tp-surface-container-highest: #f6ddd9;
--tp-on-surface: #231918;
--tp-on-surface-variant: #544340;
--tp-outline: #897370;
--tp-outline-variant: #dcc6c2;
--tp-error: #ba1a1a;
--tp-error-container: #ffdad6;
--tp-on-error-container: #410002;
--tp-accent-red: #ba1a1a;
--tp-accent-green: #4a635e;
--tp-accent-yellow: #e6a23c;
--tp-accent-blue: #4a90d9;
--tp-accent-magenta: #9c4238;
--tp-accent-cyan: #4a635e;
--elevation-0: none;
--elevation-1: 0 1px 3px rgba(60, 30, 25, 0.12), 0 1px 2px rgba(60, 30, 25, 0.08);
--elevation-2: 0 3px 6px rgba(60, 30, 25, 0.15), 0 2px 4px rgba(60, 30, 25, 0.10);
--elevation-3: 0 6px 12px rgba(60, 30, 25, 0.18), 0 4px 8px rgba(60, 30, 25, 0.12);
--bg_color: #fff8f6;
--fg_color: #231918;
--fg_faded: #544340;
--fg_dark: #9c4238;
--fg_nav: #9c4238;
--bg_panel: #fff0ed;
--bg_elements: #ffe9e5;
--bg_overlays: #fce3de;
--bg_hover: #fce3de;
--grey: #544340;
--dark_grey: #dcc6c2;
--darker_grey: #dcc6c2;
--darkest_grey: #f6ddd9;
--border_grey: #dcc6c2;
--accent: #9c4238;
--accent_light: #ff8a7a;
--accent_dark: #7a332b;
--accent_border: #ffdad4;
--play_button: #9c4238;
--play_button_hover: #3a0a06;
--more_replies_dots: #9c4238;
--error_red: #ffdad6;
--verified_blue: #4a90d9;
--verified_business: #e6a23c;
--verified_government: #8a8a8a;
--icon_text: #ffffff;
--tab: #544340;
--tab_selected: #9c4238;
--profile_stat: #231918;
--shadow: rgba(60, 30, 25, 0.12);
--shadow_dark: rgba(60, 30, 25, 0.24);
background-color: var(--bg_color);
color: var(--fg_color);
}
nav {
background-color: var(--tp-surface-container-low);
}
.search-bar > form:focus-within {
background-color: var(--tp-surface-container-lowest);
}
+194 -77
View File
@@ -5,37 +5,51 @@
border-radius: 0;
position: relative;
width: 100%;
padding: 0 8px;
}
.timeline {
background-color: var(--bg_panel);
& > div:not(:first-child) {
border-top: 1px solid var(--border_grey);
}
background-color: transparent;
}
.timeline-header {
width: 100%;
background-color: var(--bg_panel);
/* background-color: var(--tp-surface-container); */
text-align: center;
padding: 8px;
/* padding: 16px; */
display: block;
font-weight: bold;
margin-bottom: 5px;
font-weight: 600;
font-size: 14px;
/* margin-bottom: 8px; */
box-sizing: border-box;
border-radius: var(--shape-lg);
/* box-shadow: var(--elevation-1); */
color: var(--tp-on-surface);
}
& button {
float: unset;
}
.timeline-header button {
float: unset;
background: var(--tp-primary);
color: var(--tp-on-primary);
border-radius: var(--shape-sm);
padding: 8px 20px;
font-weight: 500;
transition: background 0.15s ease;
}
.timeline-header button:hover {
background: var(--tp-on-primary-container);
}
.timeline-banner img {
width: 100%;
border-radius: var(--shape-lg);
}
.timeline-description {
font-weight: normal;
color: var(--tp-on-surface-variant);
}
.tab {
@@ -43,115 +57,163 @@
display: flex;
flex-wrap: wrap;
list-style: none;
margin: 0 0 5px 0;
background-color: var(--bg_panel);
margin: 0 0 16px 0;
padding: 0;
border-bottom: 1px solid var(--tp-outline-variant);
position: sticky;
top: 56px;
background-color: var(--bg_color);
z-index: 10;
}
.tab-item {
flex: 1 1 0;
text-align: center;
margin-top: 0;
}
& a {
border-bottom: 0.1rem solid transparent;
color: var(--tab);
display: block;
padding: 8px 0;
text-decoration: none;
font-weight: bold;
.tab-item a {
color: var(--tp-on-surface-variant);
display: block;
padding: 8px 12px;
text-decoration: none;
font-weight: 500;
font-size: 14px;
border-bottom: 2px solid transparent;
margin-bottom: -1px;
transition: color 0.15s ease;
}
&:hover {
text-decoration: none;
}
.tab-item a:hover {
text-decoration: none;
color: var(--tp-on-surface);
}
&.active {
border-bottom-color: var(--tab_selected);
color: var(--tab_selected);
}
}
.tab-item a.active {
border-bottom-color: var(--tp-primary);
color: var(--tp-on-surface);
}
&.active a {
border-bottom-color: var(--tab_selected);
color: var(--tab_selected);
}
.tab-item.active a {
border-bottom-color: var(--tp-accent-blue);
color: var(--tp-on-surface);
}
&.wide {
flex-grow: 1.2;
flex-basis: 50px;
}
.tab-item.wide {
flex-grow: 1.2;
flex-basis: 50px;
}
.timeline-footer {
background-color: var(--bg_panel);
padding: 6px 0;
background-color: var(--tp-surface-container-low);
padding: 8px 0;
border-radius: var(--shape-lg);
margin-top: 8px;
}
.timeline-protected {
text-align: center;
background-color: var(--tp-surface-container);
border-radius: var(--shape-lg);
padding: 24px 16px;
box-shadow: var(--elevation-1);
}
& p {
margin: 8px 0;
}
.timeline-protected p {
margin: 8px 0;
color: var(--tp-on-surface-variant);
}
& h2 {
color: var(--accent);
font-size: 20px;
font-weight: 600;
}
.timeline-protected h2 {
color: var(--tp-primary);
font-size: 22px;
font-weight: 600;
}
.timeline-none {
color: var(--accent);
color: var(--tp-primary);
font-size: 20px;
font-weight: 600;
text-align: center;
padding: 24px 16px;
/* box-shadow: var(--elevation-1); */
}
.timeline-end {
background-color: var(--bg_panel);
color: var(--accent);
font-size: 16px;
font-weight: 600;
background-color: var(--tp-surface-container);
color: var(--tp-on-surface-variant);
font-size: 14px;
font-weight: 500;
text-align: center;
padding: 12px;
border-radius: var(--shape-lg);
margin-top: 8px;
}
.show-more {
background-color: var(--bg_panel);
background-color: transparent;
text-align: center;
padding: 0.75em 0;
padding: 12px 0;
display: block !important;
}
& a {
background-color: var(--darkest_grey);
display: inline-block;
height: 2em;
padding: 0 2em;
line-height: 2em;
.show-more a {
background-color: transparent;
display: inline-block;
height: auto;
padding: 8px 24px;
line-height: 1.5;
color: var(--tp-primary);
font-weight: 500;
border-radius: var(--shape-sm);
transition: color 0.15s ease;
}
&:hover {
background-color: var(--darker_grey);
}
}
.show-more a:hover {
background-color: var(--bg_hover);
text-decoration: none;
border-radius: 30px;
}
.top-ref {
background-color: var(--bg_color);
background-color: var(--tp-surface);
border-top: none !important;
}
& .icon-down {
font-size: 20px;
display: flex;
justify-content: center;
text-decoration: none;
.top-ref .icon-down {
font-size: 20px;
display: flex;
justify-content: center;
text-decoration: none;
color: var(--tp-primary);
padding: 8px;
border-radius: var(--shape-sm);
width: fit-content;
margin: 0 auto;
transition: color 0.15s ease;
}
&:hover {
color: var(--accent_light);
}
.top-ref .icon-down:hover {
color: var(--tp-on-primary-container);
background-color: var(--bg_hover);
}
&::before {
transform: rotate(180deg) translateY(-1px);
}
.top-ref .icon-down::before {
transform: rotate(180deg) translateY(-1px);
}
.timeline {
animation: timeline-fade-in 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes timeline-fade-in {
from {
opacity: 0;
transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@@ -159,7 +221,62 @@
overflow-wrap: break-word;
border-left-width: 0;
min-width: 0;
padding: 0.75em;
padding: 8px;
display: flex;
position: relative;
transition: background-color 0.15s ease;
animation: item-slide-up 0.5s cubic-bezier(0.4, 0, 0.2, 1) both;
}
.timeline-item:active {
background-color: var(--tp-surface-container-highest);
}
@keyframes item-slide-up {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 600px) {
.timeline-container {
padding: 0;
}
.timeline-item {
padding: 10px;
}
.timeline-item:hover {
transform: none;
}
.timeline-header,
.timeline-footer,
.timeline-protected,
.timeline-none,
.timeline-end {
border-radius: 0;
}
.tab {
margin-left: -8px;
margin-right: -8px;
}
}
/* Reduced motion preference */
@media (prefers-reduced-motion: reduce) {
.timeline,
.timeline-item {
animation: none !important;
}
}
+6 -9
View File
@@ -56,7 +56,7 @@
flex-shrink: 2;
max-width: 80%;
font-size: 14px;
font-weight: 700;
font-weight: 500;
color: var(--fg_color);
}
@@ -78,7 +78,7 @@
.tweet-date a,
.username,
.show-more a {
color: var(--fg_dark);
color: var(--tp-accent-blue);
}
.tweet-published {
@@ -282,16 +282,12 @@ a.tweet-stat {
}
.translate-btn {
background: none;
border: none;
padding: 0;
font: inherit;
font-size: 0.85em;
color: var(--accent);
cursor: pointer;
pointer-events: all !important;
pointer-events: all;
margin-top: 4px;
display: block;
clear: both;
width: 100%;
}
.translate-btn.htmx-request {
@@ -332,5 +328,6 @@ a.tweet-stat {
&:hover {
background-color: var(--bg_hover);
border-radius: 10px;
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
.quote {
margin-top: 10px;
border: solid 1px var(--dark_grey);
border: solid 1px var(--bg_panel);
border-radius: 10px;
background-color: var(--bg_elements);
overflow: hidden;
-3
View File
@@ -3,6 +3,3 @@
src: url('/fonts/Twemoji.woff2') format('woff2');
font-display: swap;
}
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, Twemoji, fontello;
}
+2 -6
View File
@@ -843,11 +843,7 @@ impl ApiClient {
}
/// Translate a tweet using Kagi Translate API.
pub async fn kagi_translate(
&self,
tweet_id: &str,
kagi_token: &str,
) -> Result<Translation> {
pub async fn kagi_translate(&self, tweet_id: &str, kagi_token: &str) -> Result<Translation> {
use http_body_util::Full;
use hyper_rustls::HttpsConnectorBuilder;
use hyper_util::{
@@ -995,7 +991,7 @@ impl ApiClient {
tweet_id: tweet.id.to_string(),
color: String::new(),
});
if photos.len() >= 16 {
if photos.len() >= 10 {
return Ok(photos);
}
}
+2 -2
View File
@@ -155,10 +155,10 @@ async fn main() -> eyre::Result<()> {
HeaderValue::from_static(
"default-src 'none'; \
script-src 'self'; \
style-src 'self' 'unsafe-inline'; \
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; \
img-src 'self' data:; \
media-src 'self' blob:; \
font-src 'self'; \
font-src 'self' https://fonts.gstatic.com; \
connect-src 'self'; \
form-action 'self'; \
base-uri 'self'; \
+3 -5
View File
@@ -121,7 +121,8 @@ async fn status_media_redirect(Path((username, id)): Path<(String, String)>) ->
#[expect(
clippy::cognitive_complexity,
reason = "status handler has many conditional branches for caching, scrolling, pagination, and translation"
reason = "status handler has many conditional branches for caching, scrolling, pagination, and \
translation"
)]
async fn status(
State(state): State<AppState>,
@@ -534,10 +535,7 @@ async fn translate(
(!server_token.is_empty()).then(|| server_token.clone())
});
let result = state
.api
.translate_auto(&id, kagi_token.as_deref())
.await;
let result = state.api.translate_auto(&id, kagi_token.as_deref()).await;
match result {
Ok(tl) if !tl.text.is_empty() => {
+1 -1
View File
@@ -52,7 +52,7 @@ impl Prefs {
hide_pins: false,
hide_replies: false,
hide_community_notes: false,
square_avatars: false,
square_avatars: true,
use_twemoji: true,
sticky_nav: true,
proxy_media: true,
+29 -29
View File
@@ -266,41 +266,41 @@ pub struct TweetStats {
reason = "mirrors Twitter API tweet fields"
)]
pub struct Tweet {
pub id: i64,
pub thread_id: i64,
pub reply_id: i64,
pub user: User,
pub text: String,
pub id: i64,
pub thread_id: i64,
pub reply_id: i64,
pub user: User,
pub text: String,
#[serde(with = "time::serde::timestamp::option")]
pub time: Option<time::OffsetDateTime>,
pub reply: Vec<String>,
pub pinned: bool,
pub has_thread: bool,
pub available: bool,
pub tombstone: String,
pub location: String,
pub source: String,
pub stats: TweetStats,
pub retweet: Option<Box<Self>>,
pub attribution: Option<User>,
pub media_tags: Vec<User>,
pub quote: Option<Box<Self>>,
pub card: Option<Card>,
pub poll: Option<Poll>,
pub gif: Option<Gif>,
pub video: Option<Video>,
pub photos: Vec<Photo>,
pub time: Option<time::OffsetDateTime>,
pub reply: Vec<String>,
pub pinned: bool,
pub has_thread: bool,
pub available: bool,
pub tombstone: String,
pub location: String,
pub source: String,
pub stats: TweetStats,
pub retweet: Option<Box<Self>>,
pub attribution: Option<User>,
pub media_tags: Vec<User>,
pub quote: Option<Box<Self>>,
pub card: Option<Card>,
pub poll: Option<Poll>,
pub gif: Option<Gif>,
pub video: Option<Video>,
pub photos: Vec<Photo>,
/// Community note (Birdwatch) -- structured data, rendered in views.
pub note: Option<CommunityNote>,
pub note: Option<CommunityNote>,
/// Content disclosure labels (paid promotion, AI-generated media).
pub paid_promotion: bool,
pub ai_generated: bool,
pub paid_promotion: bool,
pub ai_generated: bool,
/// Edit history tweet IDs.
pub history: Vec<i64>,
pub history: Vec<i64>,
/// Text entities for expansion (mentions, hashtags, URLs).
pub entities: Vec<Entity>,
pub entities: Vec<Entity>,
/// BCP-47 language code from Twitter's language detection.
pub lang: String,
pub lang: String,
/// Whether Twitter considers this tweet translatable.
pub is_translatable: bool,
/// Pre-fetched translation for embeds (OG tags, `ActivityPub`).
+16 -6
View File
@@ -3,6 +3,7 @@ use std::sync::LazyLock;
use maud::{
DOCTYPE,
Markup,
PreEscaped,
html,
};
use regex::Regex;
@@ -10,7 +11,6 @@ use regex::Regex;
use crate::{
config::Config,
types::Prefs,
views::renderutils::icon,
};
pub const STYLE_CSS: &str = "/css/style.css";
@@ -249,13 +249,23 @@ pub fn render_navbar_full(config: &Config, rss: &str, canonical: &str, referer:
img class="site-logo" src="/logo.svg" alt=(format!("{} logo", config.server.title));
}
div class="nav-item right" {
(icon("search", "", "Search", "", "/search"))
a class="nav-icon" title="Search" href="/search" {
span class="material-symbols-outlined" { "search" }
}
@if config.config.enable_rss && !rss.is_empty() {
(icon("rss", "", "RSS Feed", "", rss))
a class="nav-icon" title="RSS Feed" href=(rss) {
span class="material-symbols-outlined" { "rss_feed" }
}
}
a class="nav-icon" title="Open in X" href=(&canonical) {
(PreEscaped(r#"<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" aria-hidden="true"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.744l7.737-8.834L1.254 2.25H8.08l4.259 5.622L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117L17.083 19.77z"/></svg>"#))
}
a class="nav-icon" title="About" href="/about" {
span class="material-symbols-outlined" { "info" }
}
a class="nav-icon" title="Preferences" href=(&settings_href) {
span class="material-symbols-outlined" { "settings" }
}
(icon("bird", "", "Open in X", "", &canonical))
(icon("info", "", "About", "", "/about"))
(icon("cog", "", "Preferences", "", &settings_href))
}
}
}
+77 -50
View File
@@ -79,74 +79,101 @@ pub fn render_preferences_form(
prefs_url: &str,
) -> Markup {
html! {
div class="overlay-panel" {
div class="overlay-panel preferences-panel" {
fieldset class="preferences" {
form method="post" action="/saveprefs" autocomplete="off" {
// Close button for animated panel
a href=(referer) class="preferences-close" {
span class="icon-close" {}
}
// Header with animation
div class="preferences-header" {
h2 { "Preferences" }
p { "Customize your teapot experience" }
}
form method="post" action="/saveprefs" autocomplete="off" class="preferences-form" id="saveprefs-form" {
input type="hidden" name="referer" value=(referer);
// Display section
legend { "Display" }
(gen_select("theme", "Theme", &prefs.theme,
&themes.iter().map(|theme| (theme.as_str(), theme.as_str())).collect::<Vec<_>>()))
(gen_checkbox("infiniteScroll", "Infinite scrolling (experimental, requires JavaScript)", prefs.infinite_scroll))
(gen_checkbox("stickyProfile", "Make profile sidebar stick to top", prefs.sticky_profile))
(gen_checkbox("stickyNav", "Keep navbar fixed to top", prefs.sticky_nav))
(gen_checkbox("bidiSupport", "Support bidirectional text (makes clicking on tweets harder)", prefs.bidi_support))
(gen_checkbox("hideTweetStats", "Hide tweet stats (replies, retweets, likes)", prefs.hide_tweet_stats))
(gen_checkbox("hideBanner", "Hide profile banner", prefs.hide_banner))
(gen_checkbox("hidePins", "Hide pinned tweets", prefs.hide_pins))
(gen_checkbox("hideReplies", "Hide tweet replies", prefs.hide_replies))
(gen_checkbox("hideCommunityNotes", "Hide community notes", prefs.hide_community_notes))
(gen_checkbox("squareAvatars", "Square profile pictures", prefs.square_avatars))
(gen_checkbox("useTwemoji", "Use Twitter emoji (Twemoji)", prefs.use_twemoji))
div class="pref-section" data-section="display" {
legend { "Display" }
div class="pref-grid" {
(gen_select("theme", "Theme", &prefs.theme, &themes.iter().map(|t| (t.as_str(), t.as_str())).collect::<Vec<_>>()))
(gen_checkbox("infiniteScroll", "Infinite scrolling (experimental, requires JavaScript)", prefs.infinite_scroll))
(gen_checkbox("stickyProfile", "Make profile sidebar stick to top", prefs.sticky_profile))
(gen_checkbox("stickyNav", "Keep navbar fixed to top", prefs.sticky_nav))
(gen_checkbox("bidiSupport", "Support bidirectional text", prefs.bidi_support))
(gen_checkbox("hideTweetStats", "Hide tweet stats", prefs.hide_tweet_stats))
(gen_checkbox("hideBanner", "Hide profile banner", prefs.hide_banner))
(gen_checkbox("hidePins", "Hide pinned tweets", prefs.hide_pins))
(gen_checkbox("hideReplies", "Hide tweet replies", prefs.hide_replies))
(gen_checkbox("hideCommunityNotes", "Hide community notes", prefs.hide_community_notes))
(gen_checkbox("squareAvatars", "Square profile pictures", prefs.square_avatars))
(gen_checkbox("useTwemoji", "Use Twitter emoji (Twemoji)", prefs.use_twemoji))
}
}
// Media section
legend { "Media" }
(gen_checkbox("mp4Playback", "Enable mp4 video playback", prefs.mp4_playback))
(gen_checkbox("muteVideos", "Mute videos by default", prefs.mute_videos))
(gen_checkbox("autoplayGifs", "Autoplay gifs", prefs.autoplay_gifs))
(gen_checkbox("proxyMedia", "Proxy media through instance (recommended for privacy)", prefs.proxy_media))
// Accessibility section
legend { "Accessibility" }
(gen_font_size_select(&prefs.font_size))
div class="pref-section" data-section="media" {
legend { "Media" }
div class="pref-grid" {
(gen_checkbox("mp4Playback", "Enable mp4 video playback", prefs.mp4_playback))
(gen_checkbox("muteVideos", "Mute videos by default", prefs.mute_videos))
(gen_checkbox("autoplayGifs", "Autoplay gifs", prefs.autoplay_gifs))
}
}
// Link replacements section
legend { "Link replacements (blank to disable)" }
(gen_input("replaceTwitter", "Twitter -> teapot", &prefs.replace_twitter, "teapot hostname"))
(gen_input("replaceYouTube", "YouTube -> Piped/Invidious", &prefs.replace_youtube, "Piped hostname"))
(gen_input("replaceReddit", "Reddit -> Teddit/Libreddit", &prefs.replace_reddit, "Teddit hostname"))
div class="pref-section" data-section="links" {
legend { "Link replacements (blank to disable)" }
(gen_input("replaceTwitter", "Twitter -> teapot", &prefs.replace_twitter, "teapot hostname"))
(gen_input("replaceYouTube", "YouTube -> Piped/Invidious", &prefs.replace_youtube, "Piped hostname"))
(gen_input("replaceReddit", "Reddit -> Teddit/Libreddit", &prefs.replace_reddit, "Teddit hostname"))
}
// Translation section
legend { "Translation" }
(gen_input("kagiToken", "Kagi session token", &prefs.kagi_token, "Paste session token here"))
p class="bookmark-note" {
"Uses Kagi Translate instead of Twitter. "
a href="https://kagi.com/settings?p=user_details" target="_blank" { "Get your token here" }
" (Session Link → copy token)"
div class="pref-section" data-section="translation" {
legend { "Translation" }
(gen_input("kagiToken", "Kagi session token", &prefs.kagi_token, "Paste session token here"))
p class="bookmark-note" {
"Uses Kagi Translate instead of Twitter. "
a href="https://kagi.com/settings?p=user_details" target="_blank" { "Get your token here" }
" (Session Link → copy token)"
}
}
// Bookmark section
legend { "Bookmark" }
p class="bookmark-note" {
"Save this URL to restore your preferences (?prefs works on all pages)"
}
pre class="prefs-code" {
(prefs_url)
}
p class="bookmark-note" {
(PreEscaped("You can override preferences with query parameters (e.g. <code>?mp4Playback=on</code>). These overrides aren't saved to cookies, and links won't retain the parameters. Intended for configuring RSS feeds and other cookieless environments. Hover over a preference to see its name."))
div class="pref-section pref-section-bookmark" data-section="bookmark" {
legend { "Bookmark" }
p class="bookmark-note" {
"Save this URL to restore your preferences (?prefs works on all pages)"
}
div class="prefs-code-wrapper" {
pre class="prefs-code" {
code { (prefs_url) }
}
button type="button" class="copy-prefs-btn" data-clipboard=(prefs_url) {
span class="icon-copy" {}
}
}
p class="bookmark-note" {
(PreEscaped("You can override preferences with query parameters (e.g. <code>?mp4Playback=on</code>). These overrides aren't saved to cookies."))
}
}
h4 class="note" { "Preferences are stored client-side using cookies without any personal information." }
button type="submit" class="pref-submit" { "Save preferences" }
}
form method="post" action="/resetprefs" class="pref-reset" {
input type="hidden" name="referer" value=(referer);
button type="submit" { "Reset preferences" }
// Actions row — reset and save side by side
div class="preferences-actions" {
form method="post" action="/resetprefs" class="pref-reset-form" {
input type="hidden" name="referer" value=(referer);
button type="submit" class="pref-reset" { "Reset preferences" }
}
button type="submit" form="saveprefs-form" class="pref-submit" { "Save preferences" }
}
h4 class="note preferences-footer-note" { "Preferences are stored client-side using cookies without any personal information." }
}
}
}
+51 -33
View File
@@ -61,15 +61,18 @@ pub fn render_profile_with_prefs(
html! {
div class="profile-tabs" {
@if !hide_banner {
div class="profile-banner" {
(render_banner(&profile.user.banner, config))
}
}
// Left sidebar with profile card and photo rail
div class=(profile_tab_class) {
(render_user_card(&profile.user, config, prefs))
// Photo rail
div class="profile-header" {
@if !hide_banner {
div class="profile-banner" {
(render_banner(&profile.user.banner, config))
}
}
(render_user_card(&profile.user, config, prefs))
}
// Photo rail positioned on the left side
@if !profile.photo_rail.is_empty() {
(render_photo_rail(&profile.photo_rail, &profile.user, config))
}
@@ -91,10 +94,7 @@ pub fn render_profile_with_prefs(
}
}
} @else {
// Tabs + timeline wrapped in timeline-container
// This is critical for the CSS flexbox layout: profile-tabs has two flex children:
// 1. profile-tab (sidebar)
// 2. timeline-container (main content with tabs + timeline)
// Timeline container - main content area
div class="timeline-container" {
(render_timeline_tabs(timeline_kind, &profile.user.username))
(render_timeline_with_pinned_and_prefs(groups, config, cursor, Some(&base_url), pinned, prefs, newer_url))
@@ -126,19 +126,24 @@ pub fn render_profile_page(
html! {
div class="profile-tabs" {
@if !hide_banner {
div class="profile-banner" {
(render_banner(&user.banner, config))
}
}
// Left sidebar
div class=(profile_tab_class) {
(render_user_card(user, config, Some(prefs)))
div class="profile-header" {
@if !hide_banner {
div class="profile-banner" {
(render_banner(&user.banner, config))
}
}
(render_user_card(user, config, Some(prefs)))
}
// Photo rail on the left sidebar
@if !photo_rail.is_empty() {
(render_photo_rail(photo_rail, user, config))
}
}
// Main content area
div class="timeline-container" {
(render_timeline_tabs(tab, &user.username))
(timeline_content)
@@ -189,18 +194,17 @@ fn render_user_card(user: &User, config: &Config, prefs: Option<&Prefs>) -> Mark
html! {
div class="profile-card" {
div class="profile-card-info" {
a class="profile-card-avatar" href=(orig_avatar_url) target="_blank" {
(gen_img(&avatar_pic, avatar_class, config))
}
a class="profile-card-avatar" href=(orig_avatar_url) target="_blank" {
(gen_img(&avatar_pic, avatar_class, config))
}
div class="profile-card-tabs-name" {
div class="profile-card-identity" {
div {
(link_user(user, "profile-card-fullname"))
" "
(verified_icon(user))
" "
(link_user(user, "profile-card-username"))
}
(link_user(user, "profile-card-username"))
}
div class="profile-card-extra" {
@@ -215,12 +219,17 @@ fn render_user_card(user: &User, config: &Config, prefs: Option<&Prefs>) -> Mark
span { (icon("location", "", "", "", "")) }
" "
@let (place, url) = parse_location(&user.location);
@let display_place = if place.chars().count() > 40 {
format!("{}…", place.chars().take(40).collect::<String>())
} else {
place.clone()
};
@if !url.is_empty() {
a href=(url) { (place) }
a href=(url) title=(place) { (display_place) }
} @else if place.contains("://") {
a href=(place) { (place) }
a href=(place) title=(place) { (display_place) }
} @else {
span { (place) }
span title=(place) { (display_place) }
}
}
}
@@ -287,25 +296,34 @@ fn render_banner(banner: &str, config: &Config) -> Markup {
/// Render photo rail showing recent media.
fn render_photo_rail(photos: &[GalleryPhoto], user: &User, config: &Config) -> Markup {
let count = formatters::format_with_commas(user.media);
let header_text = format!("{count} Photos and videos");
html! {
div class="photo-rail-card" {
div class="photo-rail-header" {
a href=(format!("/{}/media", user.username)) {
(icon("picture", &header_text, "", "", ""))
span class="photo-rail-icon-wrap" {
span class="material-symbols-outlined photo-rail-media-icon" { "photo_library" }
span class="photo-rail-media-badge" { (count) }
}
span class="photo-rail-media-label" { "Media" }
}
}
// Mobile toggle
input id="photo-rail-grid-toggle" type="checkbox";
label for="photo-rail-grid-toggle" class="photo-rail-header-mobile" {
(icon("picture", &header_text, "", "", ""))
div class="photo-rail-header-mobile-inner" {
span class="photo-rail-icon-wrap" {
span class="material-symbols-outlined photo-rail-media-icon" { "photo_library" }
span class="photo-rail-media-badge" { (count) }
}
span { "Media" }
}
(icon("down", "", "", "", ""))
}
div class="photo-rail-grid" {
@for photo in photos.iter().take(16) {
@for photo in photos.iter().take(10) {
@let photo_suffix = if photo.url.contains("format") || photo.url.contains("placeholder") { "" } else { ":thumb" };
a href=(format!("/{}/status/{}#m", user.username, photo.tweet_id)) {
(gen_img(&format!("{}{}", photo.url, photo_suffix), "", config))
+3 -3
View File
@@ -5,6 +5,7 @@ use std::{
use maud::{
Markup,
PreEscaped,
html,
};
@@ -101,9 +102,8 @@ pub fn verified_icon(user: &User) -> Markup {
let title = format!("Verified {lower} account");
html! {
div class=(format!("verified-icon {lower}")) {
(icon("circle", "", &title, "verified-icon-circle", ""))
(icon("ok", "", &title, "verified-icon-check", ""))
div class=(format!("verified-icon {lower}")) title=(title) {
(PreEscaped(r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22" width="18" height="18" aria-hidden="true"><path class="verified-badge-bg" d="M 20.396 11 c -0.018 -0.646 -0.215 -1.275 -0.57 -1.816 c -0.354 -0.54 -0.852 -0.972 -1.438 -1.246 c 0.223 -0.607 0.27 -1.264 0.14 -1.897 c -0.131 -0.634 -0.437 -1.218 -0.882 -1.687 c -0.47 -0.445 -1.053 -0.75 -1.687 -0.882 c -0.633 -0.13 -1.29 -0.083 -1.897 0.14 c -0.273 -0.587 -0.704 -1.086 -1.245 -1.44 S 11.647 1.62 11 1.604 c -0.646 0.017 -1.273 0.213 -1.813 0.568 s -0.969 0.854 -1.24 1.44 c -0.608 -0.223 -1.267 -0.272 -1.902 -0.14 c -0.635 0.13 -1.22 0.436 -1.69 0.882 c -0.445 0.47 -0.749 1.055 -0.878 1.688 c -0.13 0.633 -0.08 1.29 0.144 1.896 c -0.587 0.274 -1.087 0.705 -1.443 1.245 c -0.356 0.54 -0.555 1.17 -0.574 1.817 c 0.02 0.647 0.218 1.276 0.574 1.817 c 0.356 0.54 0.856 0.972 1.443 1.245 c -0.224 0.606 -0.274 1.263 -0.144 1.896 c 0.13 0.634 0.433 1.218 0.877 1.688 c 0.47 0.443 1.054 0.747 1.687 0.878 c 0.633 0.132 1.29 0.084 1.897 -0.136 c 0.274 0.586 0.705 1.084 1.246 1.439 c 0.54 0.354 1.17 0.551 1.816 0.569 c 0.647 -0.016 1.276 -0.213 1.817 -0.567 s 0.972 -0.854 1.245 -1.44 c 0.604 0.239 1.266 0.296 1.903 0.164 c 0.636 -0.132 1.22 -0.447 1.68 -0.907 c 0.46 -0.46 0.776 -1.044 0.908 -1.681 s 0.075 -1.299 -0.165 -1.903 c 0.586 -0.274 1.084 -0.705 1.439 -1.246 c 0.354 -0.54 0.551 -1.17 0.569 -1.816 Z"/><path class="verified-badge-check" d="M 9.662 14.85 l -3.429 -3.428 l 1.293 -1.302 l 2.072 2.072 l 4.4 -4.794 l 1.347 1.246 Z"/></svg>"#))
}
}
}