@autobusal/routes-order 1.37.8 → 1.37.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Availability/Availability.tsx +8 -1
- package/Availability/Skeleton.tsx +119 -0
- package/Availability/styles.ts +2 -1
- package/Found/Filters/styles.ts +16 -3
- package/Found/Orders/Orders.tsx +10 -6
- package/Found/Orders/Skeleton.tsx +102 -0
- package/Found/Sort/styles.ts +6 -1
- package/Sections/Sections.tsx +21 -3
- package/package.json +1 -1
- package/styles.ts +37 -0
|
@@ -17,8 +17,15 @@ interface Props {
|
|
|
17
17
|
*
|
|
18
18
|
* A week: enough that somebody asking "is there a bus this weekend" never
|
|
19
19
|
* has to expand anything, few enough that the page still ends.
|
|
20
|
+
*
|
|
21
|
+
* Edited: Claude - Date: 2026-09-16
|
|
22
|
+
*
|
|
23
|
+
* Exported now: ./Skeleton reserves height for exactly this many day-blocks,
|
|
24
|
+
* not all thirty, because days 8-30 sit inside the closed <details> below
|
|
25
|
+
* and reserve nothing themselves. One number, so the skeleton and the real
|
|
26
|
+
* table can never disagree about how many days are "above the fold".
|
|
20
27
|
*/
|
|
21
|
-
const VISIBLE = 7;
|
|
28
|
+
export const VISIBLE = 7;
|
|
22
29
|
|
|
23
30
|
/**
|
|
24
31
|
* Real departures on real dates, for the next thirty days.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { TFunction } from 'i18next';
|
|
2
|
+
import { Bone } from '../styles';
|
|
3
|
+
import { Container, Heading, Lead, Wrap, Table } from './styles';
|
|
4
|
+
import { VISIBLE } from './Availability';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
id: string
|
|
8
|
+
// Real city names where they are already known - see the call site in
|
|
9
|
+
// ../Sections/Sections, which has them from the SAME facts query this
|
|
10
|
+
// section's own heading is built from once it answers. Undefined only
|
|
11
|
+
// for the instant before that, in which case the heading is a bar rather
|
|
12
|
+
// than a guess at English words in the middle of another language.
|
|
13
|
+
from?: string
|
|
14
|
+
to?: string
|
|
15
|
+
t: TFunction<'common'>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const COLUMNS = 6;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* How many rows a day's placeholder reserves.
|
|
22
|
+
*
|
|
23
|
+
* Edited: Claude - Date: 2026-09-16
|
|
24
|
+
*
|
|
25
|
+
* Not knowable before the fetch answers - a quiet rural pair might run once
|
|
26
|
+
* a day, a trunk route ten times (see TRIPS in ./Schema, measured on
|
|
27
|
+
* Tirana-Thessaloniki). Three is not a guess pulled from nowhere: three rows
|
|
28
|
+
* across the seven VISIBLE days is twenty-one, and at this table's real
|
|
29
|
+
* padding and line-height that comes to almost exactly the ~1,290px,
|
|
30
|
+
* ~20-row block that was actually measured on production - see the
|
|
31
|
+
* height math this number produces below, which is what earns it the name
|
|
32
|
+
* "defensible" rather than "round". Under-reserving still shifts the page
|
|
33
|
+
* when the real rows land; over-reserving by a lot wastes screen before
|
|
34
|
+
* anyone has asked for anything - three is the number that keeps both
|
|
35
|
+
* errors small.
|
|
36
|
+
*/
|
|
37
|
+
const ROWS_PER_DAY = 3;
|
|
38
|
+
|
|
39
|
+
const day = (index: number): JSX.Element => (
|
|
40
|
+
<tbody key={ index }>
|
|
41
|
+
<tr>
|
|
42
|
+
<th colSpan={ COLUMNS } scope="colgroup">
|
|
43
|
+
<Bone $width={ 35 } $height={ 16 } />
|
|
44
|
+
</th>
|
|
45
|
+
</tr>
|
|
46
|
+
|
|
47
|
+
{ Array.from({ length: ROWS_PER_DAY }, (_, row) => (
|
|
48
|
+
<tr key={ row }>
|
|
49
|
+
<td><Bone $width={ 70 } /></td>
|
|
50
|
+
<td><Bone $width={ 70 } /></td>
|
|
51
|
+
<td><Bone $width={ 60 } /></td>
|
|
52
|
+
<td><Bone $width={ 80 } /></td>
|
|
53
|
+
<td><Bone $width={ 55 } /></td>
|
|
54
|
+
<td><Bone $width={ 60 } /></td>
|
|
55
|
+
</tr>
|
|
56
|
+
)) }
|
|
57
|
+
</tbody>
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* What the section looks like before /api/routes/search/availability
|
|
62
|
+
* answers.
|
|
63
|
+
*
|
|
64
|
+
* Edited: Claude - Date: 2026-09-16
|
|
65
|
+
*
|
|
66
|
+
* WHY THIS EXISTS. This section used to render nothing at all while the
|
|
67
|
+
* fetch was in flight (~1.2-1.4s) and then drop a ~1,290px block in all at
|
|
68
|
+
* once - measured CLS of 0.5172-1.1646 on pair pages, against a homepage
|
|
69
|
+
* (which has no such block) of 0.0000. The fix is not to load faster, it is
|
|
70
|
+
* to occupy the space from the FIRST paint, in the shape the real content
|
|
71
|
+
* will take, so the swap moves nothing.
|
|
72
|
+
*
|
|
73
|
+
* SAME BOXES, PLACEHOLDER CONTENT. This reuses Availability's own Container,
|
|
74
|
+
* Heading, Lead, Wrap and Table rather than a hand-measured approximation of
|
|
75
|
+
* them, so the two can never quietly drift apart - a padding change in
|
|
76
|
+
* ./styles resizes both at once. Only the day count (VISIBLE, imported
|
|
77
|
+
* rather than repeated) and the row count (ROWS_PER_DAY, above) are guesses;
|
|
78
|
+
* everything else is the real box model.
|
|
79
|
+
*
|
|
80
|
+
* aria-hidden, and no live region: a placeholder table announces nothing,
|
|
81
|
+
* because it has nothing to announce - the section's actual content is
|
|
82
|
+
* either the real table that replaces this, a few seconds later, or nothing
|
|
83
|
+
* at all.
|
|
84
|
+
*/
|
|
85
|
+
const AvailabilitySkeleton = ({ id, from, to, t }: Props): JSX.Element => (
|
|
86
|
+
<Container id={ id } className="box" aria-hidden="true">
|
|
87
|
+
<Heading>
|
|
88
|
+
{ from && to
|
|
89
|
+
? t('routes_order.availability.title', { from, to })
|
|
90
|
+
: <Bone $width={ 45 } $height={ 22 } /> }
|
|
91
|
+
</Heading>
|
|
92
|
+
|
|
93
|
+
{ /* Not data-dependent - the same translation the real Lead prints,
|
|
94
|
+
so this line never has to move when the real table lands. */ }
|
|
95
|
+
<Lead>{ t('routes_order.availability.lead') }</Lead>
|
|
96
|
+
|
|
97
|
+
<Wrap>
|
|
98
|
+
<Table>
|
|
99
|
+
<thead>
|
|
100
|
+
<tr>
|
|
101
|
+
{ /* Real column headers, not placeholders - like the lead
|
|
102
|
+
above, these come from static translations rather than
|
|
103
|
+
from the fetch, so there is no reason to guess at them. */ }
|
|
104
|
+
<th>{ t('routes_order.schedule.departure') }</th>
|
|
105
|
+
<th>{ t('routes_order.schedule.arrival') }</th>
|
|
106
|
+
<th>{ t('routes_order.schedule.duration') }</th>
|
|
107
|
+
<th>{ t('routes_order.schedule.operator') }</th>
|
|
108
|
+
<th>{ t('routes_order.availability.price') }</th>
|
|
109
|
+
<th>{ t('routes_order.availability.book') }</th>
|
|
110
|
+
</tr>
|
|
111
|
+
</thead>
|
|
112
|
+
|
|
113
|
+
{ Array.from({ length: VISIBLE }, (_, index) => day(index)) }
|
|
114
|
+
</Table>
|
|
115
|
+
</Wrap>
|
|
116
|
+
</Container>
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
export default AvailabilitySkeleton;
|
package/Availability/styles.ts
CHANGED
|
@@ -185,7 +185,8 @@ export const Price = styled.strong`
|
|
|
185
185
|
* until a customer did.
|
|
186
186
|
*/
|
|
187
187
|
export const SeatsLeft = styled.span<{ $low: boolean }>`
|
|
188
|
-
|
|
188
|
+
// Claude - 2026-09-16 (audit): xs - the xxs was 10px, under the phone floor
|
|
189
|
+
font-size: ${ props => props.theme.size.xs };
|
|
189
190
|
line-height: 1.25;
|
|
190
191
|
white-space: nowrap;
|
|
191
192
|
font-weight: ${ props => (props.$low ? 700 : 400) };
|
package/Found/Filters/styles.ts
CHANGED
|
@@ -45,15 +45,28 @@ export const Toggle = styled.button<{ $open: boolean }>`
|
|
|
45
45
|
z-index: 900;
|
|
46
46
|
|
|
47
47
|
/*
|
|
48
|
-
* Clear of the home indicator on a notched phone
|
|
49
|
-
*
|
|
48
|
+
* Clear of the home indicator on a notched phone, AND clear of the
|
|
49
|
+
* cookie notice on a narrow one.
|
|
50
|
+
*
|
|
51
|
+
* Edited: Claude - Date: 2026-09-16
|
|
52
|
+
*
|
|
53
|
+
* --obt-cookie-notice-height is published by CookieNotification
|
|
54
|
+
* (packages/common) while its own bar sits fixed to this same edge under
|
|
55
|
+
* 640px - see NOTICE_HEIGHT_VAR there. Two elements anchored to the same
|
|
56
|
+
* "bottom: 0" both being fixed to the viewport does not resolve itself by
|
|
57
|
+
* z-index, which only decides which one paints on top of the other; this
|
|
58
|
+
* button was measured sitting directly under the notice at 375px. The
|
|
59
|
+
* variable defaults to 0px, so a page without the notice - or a visitor
|
|
60
|
+
* who has already dismissed it - sees no change at all.
|
|
50
61
|
*/
|
|
51
|
-
bottom: calc(20px + env(safe-area-inset-bottom, 0px));
|
|
62
|
+
bottom: calc(20px + env(safe-area-inset-bottom, 0px) + var(--obt-cookie-notice-height, 0px));
|
|
52
63
|
|
|
53
64
|
display: flex;
|
|
54
65
|
align-items: center;
|
|
55
66
|
gap: 6px;
|
|
56
67
|
padding: 9px 16px;
|
|
68
|
+
// Claude - 2026-09-16 (audit): the 44px tap minimum - this measured 35px
|
|
69
|
+
min-height: 44px;
|
|
57
70
|
font-size: ${ props => props.theme.size.xs };
|
|
58
71
|
font-weight: 700;
|
|
59
72
|
border-radius: 100px;
|
package/Found/Orders/Orders.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TFunction } from 'i18next';
|
|
2
|
-
import { Inline } from '@autobusal/common';
|
|
3
2
|
import Route from '../Route/Route';
|
|
4
3
|
import Empty from '../Empty/Empty';
|
|
4
|
+
import Skeleton from './Skeleton';
|
|
5
5
|
import { getFavorites, getNormal } from './utilities';
|
|
6
6
|
import { sortItems, SortKey } from '../refine';
|
|
7
7
|
import { Container, ContainerNotFound, Reset } from './styles';
|
|
@@ -27,12 +27,16 @@ interface Props {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
const Orders = ({ loading, data, empty, refined, sort, type, passengers, preferredStop, step1, alternatives, alternativesLoading, t, onReset, onSave }: Props): JSX.Element => {
|
|
30
|
+
/*
|
|
31
|
+
* Edited: Claude - Date: 2026-09-16
|
|
32
|
+
*
|
|
33
|
+
* A skeleton the shape of the real list, not a one-line message - see
|
|
34
|
+
* ./Skeleton for why (measured on a dated results URL, which is never
|
|
35
|
+
* prerendered: 0.6868-1.1646 CLS from a page that renders almost nothing
|
|
36
|
+
* until the search answers, then drops every card in at once).
|
|
37
|
+
*/
|
|
30
38
|
if (loading) {
|
|
31
|
-
return
|
|
32
|
-
<div className="box">
|
|
33
|
-
<Inline text={ t('routes_order.step2.loading.routes') } />
|
|
34
|
-
</div>
|
|
35
|
-
);
|
|
39
|
+
return <Skeleton t={ t } />;
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
// Edited: Ferjolt Ozuni - Date: 2026-08-01
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { TFunction } from 'i18next';
|
|
2
|
+
import { Bone } from '../../styles';
|
|
3
|
+
import { Container as Card, Trip, Company, Location, Time, Price, Amount } from '../Route/styles';
|
|
4
|
+
import { Container } from './styles';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* How many placeholder cards to reserve room for.
|
|
8
|
+
*
|
|
9
|
+
* Edited: Claude - Date: 2026-09-16
|
|
10
|
+
*
|
|
11
|
+
* Not knowable before the search answers - see ROWS_PER_DAY in
|
|
12
|
+
* ../../Availability/Skeleton for the same problem one section over. Five
|
|
13
|
+
* is the same kind of middle estimate: enough to fill roughly one screen on
|
|
14
|
+
* a desktop, which is where a reserved-but-empty block would otherwise be
|
|
15
|
+
* most visible, and not so many that a route with genuinely few departures
|
|
16
|
+
* reserves a page and a half of blank cards. A result count either side of
|
|
17
|
+
* five is a residual shift of one card's height, not the ~20-card collapse
|
|
18
|
+
* this exists to prevent.
|
|
19
|
+
*/
|
|
20
|
+
const CARDS = 5;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* One placeholder card, in the shape Route/Route.tsx renders.
|
|
24
|
+
*
|
|
25
|
+
* Edited: Claude - Date: 2026-09-16
|
|
26
|
+
*
|
|
27
|
+
* SAME STYLED COMPONENTS AS THE REAL CARD - Card, Trip, Company, Location,
|
|
28
|
+
* Time, Price, Amount all come from ../Route/styles, not a redrawn
|
|
29
|
+
* approximation of them. That is what keeps this accurate as the card
|
|
30
|
+
* itself changes: a padding or gap edited there resizes the skeleton at the
|
|
31
|
+
* same time, rather than the two silently drifting apart.
|
|
32
|
+
*
|
|
33
|
+
* The three bars inside Price carry the REAL card's own class names
|
|
34
|
+
* (price-notice, details-toggle, seats-left) so the same nth-child/class
|
|
35
|
+
* ordering rules in Price's CSS lay them out exactly as they lay out the
|
|
36
|
+
* real amount, tax note, button, details toggle and seat count - including
|
|
37
|
+
* the extra rows those last two add on a phone, which is most of what made
|
|
38
|
+
* the old three-line estimate come up short.
|
|
39
|
+
*/
|
|
40
|
+
const SkeletonCard = (): JSX.Element => (
|
|
41
|
+
<Card className="box" $type="normal" aria-hidden="true">
|
|
42
|
+
<Trip>
|
|
43
|
+
<Company>
|
|
44
|
+
<Bone $width={ 45 } $height={ 28 } />
|
|
45
|
+
<Bone $width={ 60 } $height={ 14 } />
|
|
46
|
+
</Company>
|
|
47
|
+
|
|
48
|
+
<Location $side="from">
|
|
49
|
+
<Bone $width={ 75 } $height={ 17 } />
|
|
50
|
+
<Bone $width={ 90 } $height={ 15 } />
|
|
51
|
+
</Location>
|
|
52
|
+
|
|
53
|
+
<Time>
|
|
54
|
+
<Bone $width={ 40 } $height={ 30 } />
|
|
55
|
+
</Time>
|
|
56
|
+
|
|
57
|
+
<Location $side="to">
|
|
58
|
+
<Bone $width={ 75 } $height={ 17 } />
|
|
59
|
+
<Bone $width={ 90 } $height={ 15 } />
|
|
60
|
+
</Location>
|
|
61
|
+
|
|
62
|
+
<Price>
|
|
63
|
+
<Amount><Bone $width={ 60 } $height={ 26 } /></Amount>
|
|
64
|
+
<Bone $width={ 100 } $height={ 44 } />
|
|
65
|
+
<Bone className="price-notice" $width={ 85 } $height={ 14 } />
|
|
66
|
+
<Bone className="details-toggle" $width={ 55 } $height={ 16 } />
|
|
67
|
+
<Bone className="seats-left" $width={ 65 } $height={ 14 } />
|
|
68
|
+
</Price>
|
|
69
|
+
</Trip>
|
|
70
|
+
</Card>
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
interface Props {
|
|
74
|
+
t: TFunction<'common'>
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* What the results list looks like before a search answers.
|
|
79
|
+
*
|
|
80
|
+
* Edited: Claude - Date: 2026-09-16
|
|
81
|
+
*
|
|
82
|
+
* WHY THIS EXISTS. A dated results URL (?departure=...) is never
|
|
83
|
+
* prerendered - it serves the SPA shell, so the very first thing a visitor
|
|
84
|
+
* sees is whatever this component renders while `loading` is true. That
|
|
85
|
+
* used to be a single line of text ("Searching for tickets..."), which
|
|
86
|
+
* collapsed the whole page to almost nothing and then dropped every result
|
|
87
|
+
* card in at once the moment the search answered - the same shape of CLS
|
|
88
|
+
* bug as ../../Availability/Skeleton, on a page that cannot be prerendered
|
|
89
|
+
* around it.
|
|
90
|
+
*
|
|
91
|
+
* `role="status"` with the SAME loading string the old text used, rather
|
|
92
|
+
* than a live region per card: a screen reader is told once that results
|
|
93
|
+
* are loading, not read five identical placeholder cards. The cards
|
|
94
|
+
* themselves are aria-hidden - there is nothing in them to announce.
|
|
95
|
+
*/
|
|
96
|
+
const Skeleton = ({ t }: Props): JSX.Element => (
|
|
97
|
+
<Container role="status" aria-label={ t('routes_order.step2.loading.routes') }>
|
|
98
|
+
{ Array.from({ length: CARDS }, (_, index) => <SkeletonCard key={ index } />) }
|
|
99
|
+
</Container>
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
export default Skeleton;
|
package/Found/Sort/styles.ts
CHANGED
|
@@ -94,7 +94,12 @@ export const Option = styled.button<{ $active: boolean }>`
|
|
|
94
94
|
overflow: hidden;
|
|
95
95
|
text-overflow: ellipsis;
|
|
96
96
|
padding: 5px 2px;
|
|
97
|
-
|
|
97
|
+
// Claude - 2026-09-16 (audit): xs, not xs minus 2 - that was 10px, under
|
|
98
|
+
// the phone floor. (These pills are only ever painted at 640px and up -
|
|
99
|
+
// see Options in ./Sort.tsx - but the rule cascades to the hidden phone
|
|
100
|
+
// copy of every one of them regardless, and a text-size audit reads
|
|
101
|
+
// computed style, not display.)
|
|
102
|
+
font-size: ${ props => props.theme.size.xs };
|
|
98
103
|
font-weight: 700;
|
|
99
104
|
white-space: nowrap;
|
|
100
105
|
|
package/Sections/Sections.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import { TFunction } from 'i18next';
|
|
|
2
2
|
import Facts from '../Facts/Facts';
|
|
3
3
|
import Faq from '../Faq/Faq';
|
|
4
4
|
import Availability from '../Availability/Availability';
|
|
5
|
+
import AvailabilitySkeleton from '../Availability/Skeleton';
|
|
5
6
|
import AvailabilitySchema from '../Availability/Schema';
|
|
6
7
|
import Schedule from '../Schedule/Schedule';
|
|
7
8
|
import Links from '../Links/Links';
|
|
@@ -60,7 +61,12 @@ const LINKS = 'links';
|
|
|
60
61
|
const Sections = ({ from, to, heading, t }: Props): (JSX.Element | null) => {
|
|
61
62
|
const { data } = useGetFacts(from, to);
|
|
62
63
|
|
|
63
|
-
|
|
64
|
+
// Edited: Claude - Date: 2026-09-16
|
|
65
|
+
// `isLoading`, not `isFetching`: this is the ONE state the skeleton below
|
|
66
|
+
// exists for - the first request, before anything has ever answered. A
|
|
67
|
+
// background refetch re-uses `dates` from cache while it runs, so it never
|
|
68
|
+
// sees this true and never swaps a real table back out for a placeholder.
|
|
69
|
+
const { data: dates, isLoading: datesLoading } = useGetAvailability(from, to);
|
|
64
70
|
|
|
65
71
|
const facts = data?.facts;
|
|
66
72
|
|
|
@@ -180,11 +186,23 @@ const Sections = ({ from, to, heading, t }: Props): (JSX.Element | null) => {
|
|
|
180
186
|
at all when the endpoint gives us nothing, which is also what
|
|
181
187
|
happens on a pair that has stopped running: the notice in the
|
|
182
188
|
facts block above has already said so, and thirty rows of "no
|
|
183
|
-
departures" underneath it would be a wall, not an answer.
|
|
184
|
-
|
|
189
|
+
departures" underneath it would be a wall, not an answer.
|
|
190
|
+
|
|
191
|
+
Edited: Claude - Date: 2026-09-16
|
|
192
|
+
WHILE IT LOADS, a skeleton the same shape takes the space instead
|
|
193
|
+
of nothing - see Availability/Skeleton for why (measured CLS of
|
|
194
|
+
0.52-1.16 on this exact block). `datesLoading` is the only other
|
|
195
|
+
branch: a pair that answers with nothing goes straight from the
|
|
196
|
+
skeleton to rendering nothing at all, which is correct - there
|
|
197
|
+
was never a table to reserve room for. */ }
|
|
198
|
+
{ availability ? (
|
|
185
199
|
<Anchored>
|
|
186
200
|
<Availability id={ AVAILABILITY } data={ availability } t={ t } />
|
|
187
201
|
</Anchored>
|
|
202
|
+
) : datesLoading && (
|
|
203
|
+
<Anchored>
|
|
204
|
+
<AvailabilitySkeleton id={ AVAILABILITY } from={ facts.from } to={ facts.to } t={ t } />
|
|
205
|
+
</Anchored>
|
|
188
206
|
) }
|
|
189
207
|
|
|
190
208
|
<Anchored>
|
package/package.json
CHANGED
package/styles.ts
CHANGED
|
@@ -153,4 +153,41 @@ export const Listing = styled.div`
|
|
|
153
153
|
// column wider than its share of the row
|
|
154
154
|
flex: 1;
|
|
155
155
|
min-width: 0;
|
|
156
|
+
|
|
157
|
+
/*
|
|
158
|
+
* Edited: Claude - Date: 2026-09-16
|
|
159
|
+
*
|
|
160
|
+
* Real room at the FOOT of the list, on a narrow screen, for whatever the
|
|
161
|
+
* cookie notice (packages/common/CookieNotification) is currently taking
|
|
162
|
+
* up down there - not the fixed "20px" the notice used to assume, since a
|
|
163
|
+
* fixed guess is wrong the moment the sentence wraps to a different number
|
|
164
|
+
* of lines in a longer language. --obt-cookie-notice-height is the
|
|
165
|
+
* notice's own measured height, published as a CSS custom property so
|
|
166
|
+
* this column never has to ask it directly; 0px when the notice is not
|
|
167
|
+
* showing, so a visitor who has already dismissed it loses nothing here.
|
|
168
|
+
*/
|
|
169
|
+
@media (max-width: 639px) {
|
|
170
|
+
padding-bottom: var(--obt-cookie-notice-height, 0px);
|
|
171
|
+
}
|
|
172
|
+
`;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* A placeholder block, sized as a fraction of its cell.
|
|
176
|
+
*
|
|
177
|
+
* Edited: Claude - Date: 2026-09-16
|
|
178
|
+
*
|
|
179
|
+
* Shared by the two loading skeletons on this page - Availability/Skeleton
|
|
180
|
+
* and Found/Orders/Skeleton - so the one thing they agree on (what a
|
|
181
|
+
* "nothing has arrived yet" rectangle looks like) is defined once rather
|
|
182
|
+
* than twice. A flat fill, not the shimmering-gradient kind: the whole job
|
|
183
|
+
* of a skeleton here is to hold the page still while data arrives, and an
|
|
184
|
+
* animated one is itself something moving on the page for the second or so
|
|
185
|
+
* it is visible.
|
|
186
|
+
*/
|
|
187
|
+
export const Bone = styled.span<{ $width: number, $height?: number }>`
|
|
188
|
+
display: inline-block;
|
|
189
|
+
width: ${ props => props.$width }%;
|
|
190
|
+
height: ${ props => props.$height ?? 14 }px;
|
|
191
|
+
border-radius: 4px;
|
|
192
|
+
background: ${ props => props.theme.background.neutral };
|
|
156
193
|
`;
|