an app to share curated trails sidetrail.app
atproto nextjs react rsc
50
fork

Configure Feed

Select the types of activity you want to include in your feed.

improve touch support

+230 -59
+22 -7
app/BackButton.css
··· 14 14 text-align: center; 15 15 } 16 16 17 - .BackButton:hover { 17 + @media (hover: hover) { 18 + .BackButton:hover { 19 + color: var(--accent-color); 20 + } 21 + } 22 + 23 + .BackButton:active { 18 24 color: var(--accent-color); 25 + transition-duration: 0.05s; 19 26 } 20 27 21 28 .BackButton-top { ··· 28 35 text-decoration 0.2s ease; 29 36 } 30 37 31 - .BackButton-top:hover { 32 - background: linear-gradient( 33 - to bottom, 34 - color-mix(in srgb, var(--accent-color) 12%, transparent), 35 - transparent 36 - ); 38 + @media (hover: hover) { 39 + .BackButton-top:hover { 40 + background: linear-gradient( 41 + to bottom, 42 + color-mix(in srgb, var(--accent-color) 12%, transparent), 43 + transparent 44 + ); 45 + text-decoration: underline; 46 + text-decoration-color: var(--accent-color); 47 + } 48 + } 49 + 50 + .BackButton-top:active { 37 51 text-decoration: underline; 38 52 text-decoration-color: var(--accent-color); 53 + transition-duration: 0.05s; 39 54 }
+46 -10
app/EditButtons.css
··· 1 - /* Base utility button - shared styles */ 2 1 .edit-btn-utility { 3 2 width: 32px; 4 3 height: 32px; ··· 20 19 filter: var(--user-content-filter); 21 20 } 22 21 23 - .edit-btn-utility:hover { 22 + @media (max-width: 768px) { 23 + .edit-btn-utility::after { 24 + content: ""; 25 + position: absolute; 26 + inset: -6px; 27 + } 28 + } 29 + 30 + @media (hover: hover) { 31 + .edit-btn-utility:hover { 32 + background: var(--accent-color); 33 + color: white; 34 + transform: scale(1.05); 35 + } 36 + } 37 + 38 + .edit-btn-utility:active { 24 39 background: var(--accent-color); 25 40 color: white; 26 - transform: scale(1.05); 41 + transition-duration: 0.05s; 27 42 } 28 43 29 - /* Add button - larger icon, adjusted centering */ 30 44 .edit-btn-add { 31 45 font-size: 1.25rem; 32 46 line-height: 0.9; 33 47 padding-bottom: 2px; 34 48 } 35 49 36 - /* Delete button - subtle style */ 37 50 .edit-btn-delete { 38 51 width: 28px; 39 52 height: 28px; ··· 50 63 font-weight: 300; 51 64 line-height: 1; 52 65 padding: 0; 53 - padding-bottom: 3px; /* Adjust vertical alignment of × */ 66 + padding-bottom: 3px; 54 67 font-family: inherit; 68 + position: relative; 55 69 } 56 70 57 - .edit-btn-delete:hover { 58 - color: var(--text-secondary); 59 - background: rgba(0, 0, 0, 0.05); 71 + @media (max-width: 768px) { 72 + .edit-btn-delete::after { 73 + content: ""; 74 + position: absolute; 75 + inset: -8px; 76 + } 60 77 } 61 78 62 - @media (prefers-color-scheme: dark) { 79 + @media (hover: hover) { 80 + .edit-btn-delete:hover { 81 + color: var(--text-secondary); 82 + background: rgba(0, 0, 0, 0.05); 83 + } 84 + } 85 + 86 + @media (hover: hover) and (prefers-color-scheme: dark) { 63 87 .edit-btn-delete:hover { 64 88 background: rgba(255, 255, 255, 0.05); 89 + } 90 + } 91 + 92 + .edit-btn-delete:active { 93 + color: var(--text-secondary); 94 + background: rgba(0, 0, 0, 0.08); 95 + transition-duration: 0.05s; 96 + } 97 + 98 + @media (prefers-color-scheme: dark) { 99 + .edit-btn-delete:active { 100 + background: rgba(255, 255, 255, 0.08); 65 101 } 66 102 } 67 103
+12 -5
app/NewTrailButton.css
··· 12 12 font-weight: 500; 13 13 } 14 14 15 - .NewTrailButton:hover { 16 - border-color: var(--text-primary); 17 - color: var(--text-primary); 18 - background: rgba(0, 0, 0, 0.02); 15 + @media (hover: hover) { 16 + .NewTrailButton:hover { 17 + border-color: var(--text-primary); 18 + color: var(--text-primary); 19 + background: rgba(0, 0, 0, 0.02); 20 + } 19 21 } 20 22 21 - @media (prefers-color-scheme: dark) { 23 + @media (hover: hover) and (prefers-color-scheme: dark) { 22 24 .NewTrailButton:hover { 23 25 background: rgba(255, 255, 255, 0.05); 24 26 } 25 27 } 28 + 29 + .NewTrailButton:active { 30 + border-color: var(--text-primary); 31 + transition-duration: 0.05s; 32 + }
+9 -2
app/SegmentTabs.css
··· 22 22 .SegmentTabs-tab { 23 23 background: transparent; 24 24 border: none; 25 - padding: 0; 25 + padding: 0.5rem 0; 26 26 font-size: 1.125rem; 27 27 color: var(--text-tertiary); 28 28 cursor: pointer; ··· 34 34 position: relative; 35 35 } 36 36 37 - .SegmentTabs-tab:hover:not(.SegmentTabs-tab--active) { 37 + @media (hover: hover) { 38 + .SegmentTabs-tab:hover:not(.SegmentTabs-tab--active) { 39 + color: var(--text-secondary); 40 + } 41 + } 42 + 43 + .SegmentTabs-tab:active:not(.SegmentTabs-tab--active) { 38 44 color: var(--text-secondary); 45 + transition-duration: 0.05s; 39 46 } 40 47 41 48 .SegmentTabs-tab--active {
+11 -4
app/TrailCard.css
··· 67 67 z-index: -1; 68 68 } 69 69 70 - .TrailCard:hover .TrailCard-bg { 71 - transform: translateY(-2px); 70 + @media (hover: hover) { 71 + .TrailCard:hover .TrailCard-bg { 72 + transform: translateY(-2px); 73 + } 74 + 75 + .TrailCard:hover .TrailCard-bg::before { 76 + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); 77 + border-color: var(--accent-color); 78 + } 72 79 } 73 80 74 - .TrailCard:hover .TrailCard-bg::before { 75 - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); 81 + .TrailCard:active .TrailCard-bg::before { 76 82 border-color: var(--accent-color); 83 + transition-duration: 0.05s; 77 84 } 78 85 79 86 .TrailCard-title {
+9 -2
app/at/(trail)/[handle]/trail/[rkey]/AccentButton.css
··· 36 36 min-width: 160px; 37 37 } 38 38 39 - .AccentButton:hover:not(:disabled) { 40 - transform: translateY(-2px) scale(1.02); 39 + @media (hover: hover) { 40 + .AccentButton:hover:not(:disabled) { 41 + transform: translateY(-2px) scale(1.02); 42 + } 43 + } 44 + 45 + .AccentButton:active:not(:disabled) { 46 + transform: scale(0.98); 47 + transition-duration: 0.05s; 41 48 } 42 49 43 50 .AccentButton:disabled {
+18 -3
app/at/(trail)/[handle]/trail/[rkey]/LinkPreview.css
··· 29 29 transition: all 0.2s; 30 30 } 31 31 32 - .LinkPreview:hover::before { 32 + @media (hover: hover) { 33 + .LinkPreview:hover::before { 34 + border-color: var(--accent-color, #999); 35 + filter: var(--user-content-filter); 36 + } 37 + } 38 + 39 + .LinkPreview:active::before { 33 40 border-color: var(--accent-color, #999); 34 41 filter: var(--user-content-filter); 42 + transition-duration: 0.05s; 35 43 } 36 44 37 45 .LinkPreview--simple .LinkPreview-info { ··· 108 116 padding: 0; 109 117 } 110 118 111 - .LinkPreview-deleteButton:hover { 112 - color: white; 119 + @media (hover: hover) { 120 + .LinkPreview-deleteButton:hover { 121 + color: white; 122 + background: rgba(0, 0, 0, 0.7); 123 + } 124 + } 125 + 126 + .LinkPreview-deleteButton:active { 113 127 background: rgba(0, 0, 0, 0.7); 128 + transition-duration: 0.05s; 114 129 } 115 130 116 131 .LinkPreview-deleteButton:focus-visible {
+50 -15
app/at/(trail)/[handle]/trail/[rkey]/TrailProgress.css
··· 40 40 height: 8px; 41 41 border-radius: 50%; 42 42 transition: all 0.4s ease; 43 + position: relative; 43 44 } 44 45 45 46 .TrailProgress-node--clickable { 46 47 cursor: pointer; 47 48 } 48 49 49 - .TrailProgress-node--clickable:hover { 50 - transform: scale(1.2); 50 + @media (max-width: 768px) { 51 + .TrailProgress-node--clickable::after { 52 + content: ""; 53 + position: absolute; 54 + inset: -12px; 55 + } 56 + } 57 + 58 + @media (hover: hover) { 59 + .TrailProgress-node--clickable:hover { 60 + transform: scale(1.2); 61 + } 62 + } 63 + 64 + .TrailProgress-node--clickable:active { 65 + transform: scale(1.3); 66 + transition-duration: 0.1s; 51 67 } 52 68 53 69 .TrailProgress-node--upcoming { ··· 80 96 } 81 97 } 82 98 83 - .TrailProgress-node--current.TrailProgress-node--clickable:hover { 84 - transform: scale(1.4); 99 + @media (hover: hover) { 100 + .TrailProgress-node--current.TrailProgress-node--clickable:hover { 101 + transform: scale(1.4); 102 + } 85 103 } 86 104 87 105 .TrailProgress-node--completed { ··· 96 114 filter: var(--user-content-filter); 97 115 } 98 116 99 - .TrailProgress-node--visited.TrailProgress-node--clickable:hover { 100 - transform: scale(1.2); 101 - opacity: 0.7; 117 + @media (hover: hover) { 118 + .TrailProgress-node--visited.TrailProgress-node--clickable:hover { 119 + transform: scale(1.2); 120 + opacity: 0.7; 121 + } 102 122 } 103 123 104 124 .TrailProgress-line { ··· 122 142 line-height: 1; 123 143 white-space: nowrap; 124 144 text-decoration: none; 145 + padding: 0.5rem 0; 125 146 transition: 126 147 color 0.2s ease, 127 148 text-decoration 0.2s ease, 128 149 filter 0.2s ease; 129 150 } 130 151 131 - .TrailProgress-statusWalking:hover { 152 + @media (hover: hover) { 153 + .TrailProgress-statusWalking:hover { 154 + color: var(--accent-color); 155 + text-decoration: underline; 156 + text-decoration-color: var(--accent-color); 157 + filter: var(--user-content-filter); 158 + } 159 + } 160 + 161 + .TrailProgress-statusWalking:active { 132 162 color: var(--accent-color); 133 - text-decoration: underline; 134 - text-decoration-color: var(--accent-color); 135 - filter: var(--user-content-filter); 163 + transition-duration: 0.05s; 136 164 } 137 165 138 166 .TrailProgress-statusLeave { 139 167 font-family: inherit; 140 168 font-size: 0.6875rem; 141 169 line-height: 1; 142 - padding: 0; 170 + padding: 0.5rem 0; 143 171 background: none; 144 172 border: none; 145 173 color: var(--text-muted); ··· 149 177 white-space: nowrap; 150 178 } 151 179 152 - .TrailProgress-statusLeave:hover { 180 + @media (hover: hover) { 181 + .TrailProgress-statusLeave:hover { 182 + color: var(--accent-color); 183 + text-decoration: underline; 184 + filter: var(--user-content-filter); 185 + } 186 + } 187 + 188 + .TrailProgress-statusLeave:active { 153 189 color: var(--accent-color); 154 - text-decoration: underline; 155 - filter: var(--user-content-filter); 190 + transition-duration: 0.05s; 156 191 } 157 192 158 193 /* Tablet and desktop enhancements */
+16 -5
app/at/(trail)/[handle]/trail/[rkey]/TrailStop.css
··· 38 38 cursor: pointer; 39 39 } 40 40 41 - .TrailStop--visited:hover .TrailStop-content { 41 + @media (hover: hover) { 42 + .TrailStop--visited:hover .TrailStop-content { 43 + opacity: 0.7; 44 + } 45 + } 46 + 47 + .TrailStop--visited:active .TrailStop-content { 42 48 opacity: 0.7; 49 + transition-duration: 0.05s; 43 50 } 44 51 45 52 .TrailStop--current { ··· 124 131 opacity: 1; 125 132 } 126 133 127 - .TrailStop-reorderControls:hover { 128 - opacity: 1; 134 + @media (hover: hover) { 135 + .TrailStop-reorderControls:hover { 136 + opacity: 1; 137 + } 129 138 } 130 139 131 140 .TrailStop-reorderControls > * { ··· 165 174 z-index: -1; 166 175 } 167 176 168 - .TrailStop-insertButton--between:hover { 169 - opacity: 1; 177 + @media (hover: hover) { 178 + .TrailStop-insertButton--between:hover { 179 + opacity: 1; 180 + } 170 181 } 171 182 172 183 /* Final button stays always visible - never dimmed */
+20 -6
app/at/(trail)/[handle]/trail/[rkey]/TrailWalk.css
··· 143 143 .TrailWalk-abandonButton { 144 144 font-family: inherit; 145 145 font-size: 0.875rem; 146 - padding: 0; 146 + padding: 0.75rem 1rem; 147 147 background-color: transparent; 148 148 color: var(--text-muted); 149 149 border: none; ··· 152 152 text-transform: lowercase; 153 153 } 154 154 155 - .TrailWalk-abandonButton:hover { 155 + @media (hover: hover) { 156 + .TrailWalk-abandonButton:hover { 157 + color: var(--text-secondary); 158 + text-decoration: underline; 159 + } 160 + } 161 + 162 + .TrailWalk-abandonButton:active { 156 163 color: var(--text-secondary); 157 - text-decoration: underline; 164 + transition-duration: 0.05s; 158 165 } 159 166 160 167 .TrailWalk-publishButton { ··· 172 179 filter: var(--user-content-filter); 173 180 } 174 181 175 - .TrailWalk-publishButton:hover:not(:disabled) { 176 - transform: translateY(-2px); 177 - box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2); 182 + @media (hover: hover) { 183 + .TrailWalk-publishButton:hover:not(:disabled) { 184 + transform: translateY(-2px); 185 + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2); 186 + } 187 + } 188 + 189 + .TrailWalk-publishButton:active:not(:disabled) { 190 + transform: scale(0.98); 191 + transition-duration: 0.05s; 178 192 } 179 193 180 194 .TrailWalk-publishButton:disabled {
+17
app/global.css
··· 24 24 box-sizing: border-box; 25 25 } 26 26 27 + /* ========== TOUCH DEVICE FIXES ========== */ 28 + 29 + a, 30 + button, 31 + [role="button"], 32 + input, 33 + select, 34 + textarea { 35 + -webkit-tap-highlight-color: transparent; 36 + } 37 + 38 + a, 39 + button, 40 + [role="button"] { 41 + touch-action: manipulation; 42 + } 43 + 27 44 *:focus-visible { 28 45 outline-color: var(--accent-color, #5f8ec4); 29 46 outline-offset: 2px;