Skip to content

Commit 5f70776

Browse files
Merge branch 'main-check-merge' into github-private-npm-module-deployment
2 parents 06a6e3a + b180e23 commit 5f70776

File tree

18 files changed

+2887
-315
lines changed

18 files changed

+2887
-315
lines changed

docs/ResponsiveDesign.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Introduction
2+
3+
Plain HTML will work on any screen. Therefore, failure to display content properly on different screen sizes is generally caused by CSS [^1].
4+
5+
[^1] [Responsive columns without media queries](https://medium.com/@hayavuk/responsive-columns-without-media-queries-1dd92dc0f5e6)
6+
7+
# Breakpoints and media queries
8+
9+
> Breakpoints should be content-based, not device based - *Kaloyan Kosev*[^1]
10+
11+
> Start with the small screen first, then expand until it looks like s\*\*t. Time for a breakpoint! – *Stephen Hay*[^3]
12+
13+
You don't want everything to scale proportionally. On larger screens, images can become very large, forcing other content off the screen[^4].
14+
15+
It's common to use viewport width in media queries, but there are a large range of media features that can be detected (color, color index, aspect ratio, device aspect ratio, width, device width, height, device height, orientation, monochrome, resolution, scan, pixel-density, and more)[^3].
16+
17+
There are several reasons not to use device pixel dimensions as breakpoints. One is that new devices are likely to have different dimensions, making the older ones abritrary[^3].
18+
19+
Sometimes a design needs to change significantly when the available space (or other parameter) renders it unsuitable. Other times, it might only be necessary to change one aspect. This is known as a minor breakpoint or a tweakpoint [^3].
20+
21+
Developers are warned that highly complex media queries pose a danger: they can be difficult to manage long-term [^3].
22+
23+
Older advice on the internet suggests that pixel-based media queries didn't function as expected when a user zoomed in. This issue is now outdated. However, it's still recommended to use em-based media queries because they will still function when a browser's font size isn't the default 16px [^1]
24+
25+
It's recommended that lines of text aren't too short or long, ideally 45-90 characters. It's possible to achieve this with different font sizes by specifying font size and element width as a proportion of viewport width, and scaling them at the same rate [^3].
26+
27+
Progressive enhancement is...
28+
Min-width is better suited to progressive enhancement than max-width, because styles are overridden at smaller screens, not larger ones[^5]
29+
30+
[^1] [Journey to highly effective and maintainable CSS media queries](https://notes.devlabs.bg/journey-to-highly-effective-and-maintainable-css-media-queries-876e5b92f918)
31+
32+
[^2] [7 habits of highly effective media queries](https://bradfrost.com/blog/post/7-habits-of-highly-effective-media-queries/)
33+
34+
[^3] [Responsive web design](https://practicaltypography.com/responsive-web-design.html)
35+
36+
[^4] [Responsive web design](https://alistapart.com/article/responsive-web-design/)
37+
38+
[^5] [Avoiding common mistakes with CSS media queries](https://blog.pixelfreestudio.com/avoiding-common-mistakes-with-css-media-queries/)
39+
40+
# Units
41+
42+
Units are used to define the size of elements on a screen. Using appropriate units is important for ensuring designs scale properly on different screen sizes.
43+
44+
rem units are defined by the font size of the root element (16px by default).
45+
em units are defined by the font size of the parent element.
46+
47+
Font sizes should be set using rem units; values in pixels won't scale properly if the browser's font size changes. By default, 1rem = 16px, so 1.5rem = 24px.
48+
49+
Margins and padding should be set using em units.
50+
51+
When defining CSS width and height values, percentages are recommended (as a proportion of the parent element).
52+
53+
Properties that don't need to be responsive (e.g. border-width) can be set using pixels.
54+
55+
From: [Why CSS units matter to your responsive website designs](https://pieces.app/blog/css-units-responsive-website-designs)
56+
57+
See also: [whatunit.com](https://whatunit.com/)
58+
59+
# Interaction
60+
61+
Touch targets should be large enough (Apple recomends 44x44 pixels[^1]) and should be sufficient spaced apart [^2]
62+
63+
[^1] [A Hands-On Guide to Mobile-First Design](https://www.uxpin.com/studio/blog/a-hands-on-guide-to-mobile-first-design/)
64+
[^2] [Common Mobile Design Mistake on Your Website](https://conroycreativecounsel.com/avoid-these-common-mobile-design-mistakes-on-your-website/)
65+
66+
# Progressive Enhancement
67+
68+
# Common Pitfalls of Mobile-First
69+
70+
> Porting an unchanged UI to a different platform hurts UX. - *NN Group*[^3]
71+
72+
A one-size-fits-all approach might be appealing, but it's possible to focus on smaller screens *at the expense of* larger screens. Desktops are more 'robust' - they can handle more complexity.
73+
74+
This involves dailing to 'flesh out' a design for screens that can handle it [^1]. This is known as 'mobile everything'[^2] or 'mobile-only'[^3].
75+
76+
Consider a hamburger icon to find the links to toggle between views. On mobile, there might be insufficient space, but not so on desktop. Displaying these links permanently would reduce clicks from one to two [^2]. Futhermore, interactive elements like a hamburger icon might be easily noticed on a mobile screen, but overlooked on a large desktop screen [^3].
77+
78+
Other examples include hiding functionality in drawers, using icons instead of/without text, and breaking content into several screens [^2].
79+
80+
Research has found that navigation isn't used as frequently when it's hidden within a menu [^3].
81+
82+
Some argue that the importance of consistency across devices is overestimated [^1].
83+
The context is different on different devices, so the interfaces should be adapted to suit the context [^1].
84+
Input differs across devices too, so design should be modified accordingly. Phones are typically used in portrait mode, and thumbs will be able to respond to functionality near the bottom. Tablets are often used with both hands, so there's greater scope for where interactive elements are placed. Desktop users have more fine-grained control, due to using a mouse or trackpad. Desktop users might expect key functionality to be positioned towards the top of the screen [^1].
85+
86+
[^1] [Why is mobile-first design failing](https://jamesarcher.co/170/mobile-first-why-are-we-getting-it-wrong)
87+
[^3] [Mobile-first not mobile-only](https://www.nngroup.com/articles/mobile-first-not-mobile-only/)
88+
[^2] [Mobile first, desktop worst](https://blog.prototypr.io/mobile-first-desktop-worst-f900909ae9e2)

package-lock.json

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
"accessible-autocomplete": "^3.0.1",
9292
"csv-parser": "^3.0.0",
9393
"d3": "^7.9.0",
94+
"decimal.js": "^10.5.0",
9495
"dompurify": "^3.2.5",
9596
"flowbite-svelte-icons": "^2.0.3",
9697
"govuk-frontend": "^5.9.0",

src/lib/components/data-vis/axis/Ticks.svelte

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script>
22
import { tick } from "svelte";
3+
import Decimal from "decimal.js";
34
45
let {
56
ticksArray = $bindable(),
@@ -10,35 +11,45 @@
1011
axisFunction,
1112
values,
1213
numberOfTicks,
13-
baseline,
14-
setMax,
14+
minTick,
15+
maxTick,
1516
orientation,
1617
yearsInput,
1718
generateTicksNum,
19+
yearFormating,
1820
} = $props();
1921
2022
$inspect(ticksArray);
2123
22-
function generateTicks(data, numTicks, baseline, setTick) {
23-
console.log(data, typeof data);
24-
let minVal = baseline !== null ? baseline : Math.min(...data);
25-
let maxVal = setTick !== null ? setTick : Math.max(...data);
26-
let rangeVal = maxVal - minVal;
24+
function generateTicks(data, numTicks, minTick, maxTick) {
25+
let minVal =
26+
minTick !== null
27+
? new Decimal(minTick)
28+
: Decimal.min(...data.map((val) => new Decimal(val)));
29+
let maxVal =
30+
maxTick !== null
31+
? new Decimal(maxTick)
32+
: Decimal.max(...data.map((val) => new Decimal(val)));
33+
let rangeVal = maxVal.minus(minVal);
2734
28-
let roughStep = rangeVal / (numTicks - 1);
35+
let roughStep = rangeVal.div(numTicks - 1);
2936
let normalizedSteps = [1, 2, 5, 10];
3037
31-
let stepPower = Math.pow(10, -Math.floor(Math.log10(roughStep)));
32-
let normalizedStep = roughStep * stepPower;
33-
let optimalStep =
34-
normalizedSteps.find((step) => step >= normalizedStep) / stepPower;
38+
let stepPower = Decimal.pow(
39+
10,
40+
-Math.floor(Math.log10(roughStep.toNumber())),
41+
);
42+
let normalizedStep = roughStep.mul(stepPower);
43+
let optimalStep = new Decimal(
44+
normalizedSteps.find((step) => step >= normalizedStep.toNumber()),
45+
).div(stepPower);
3546
36-
let scaleMin = Math.floor(minVal / optimalStep) * optimalStep;
37-
let scaleMax = Math.ceil(maxVal / optimalStep) * optimalStep;
47+
let scaleMin = minVal.div(optimalStep).floor().mul(optimalStep);
48+
let scaleMax = maxVal.div(optimalStep).ceil().mul(optimalStep);
3849
3950
let ticks = [];
40-
for (let i = scaleMin; i <= scaleMax; i += optimalStep) {
41-
ticks.push(i);
51+
for (let i = scaleMin; i.lte(scaleMax); i = i.plus(optimalStep)) {
52+
ticks.push(i.toNumber());
4253
}
4354
return ticks;
4455
}
@@ -53,7 +64,7 @@
5364
5465
numberOfTicks = tickCount(chartWidth);
5566
56-
ticksArray = generateTicks(values, numberOfTicks, baseline, setMax);
67+
ticksArray = generateTicks(values, numberOfTicks, minTick, maxTick);
5768
let yearTicks = yearsInput ? yearsFormat(ticksArray) : [];
5869
</script>
5970

@@ -93,7 +104,7 @@
93104
: "start"}
94105
fill="black"
95106
>
96-
{yearsInput ? yearTicks[index] : `${prefix}${tick}${suffix}`}
107+
{yearsInput ? yearTicks[index] : "${prefix{tick}{suffix}"}
97108
</text>
98109
</g>
99110
{/each}

src/lib/components/data-vis/line-chart/Line.svelte

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,29 @@
2121
pathFillColor = "none",
2222
pathStrokeDashArray = "none",
2323
areaFillColor,
24-
includeArea,
24+
includeArea = false,
2525
includeMarkers = false,
2626
markerShape = "circle",
2727
markerRadius = 5,
2828
markerFill = "grey",
2929
markerStroke = "white",
3030
markerStrokeWidth = 3,
3131
lineFunction,
32+
areaFunction,
3233
curveFunction,
3334
xFunction,
3435
lineEnding = null,
3536
yFunction,
3637
dataId,
38+
tier,
3739
// markersDataId,
3840
onClick,
3941
onMouseEnter,
4042
onMouseLeave,
41-
onMouseMove,
4243
halo,
43-
chartBackgroundColor = "#d1d1d1",
44+
chartBackgroundColor,
45+
invisibleStrokeWidth,
46+
interactive,
4447
// onClickMarker,
4548
// onMouseEnterMarker,
4649
// onMouseLeaveMarker,
@@ -73,14 +76,6 @@
7376
function onMouseLeaveMarker(i) {
7477
hoveredMarker = null;
7578
}
76-
77-
let areaFunction = $derived(
78-
area()
79-
.y0((d) => yFunction(0))
80-
.x((d) => xFunction(d.x))
81-
.y1((d) => yFunction(d.y))
82-
.curve(curveLinear),
83-
);
8479
</script>
8580
8681
<defs>
@@ -112,7 +107,6 @@
112107
onclick={(event) => onClick(event, dataArray, dataId)}
113108
onmouseenter={(event) => onMouseEnter(event, dataArray, dataId)}
114109
onmouseleave={(event) => onMouseLeave(event, dataArray, dataId)}
115-
onmousemove={(event) => onMouseMove(event, dataArray, dataId)}
116110
role="button"
117111
tabindex="0"
118112
onkeydown={(e) => e.key === "Enter" && onClick(e, dataArray)}
@@ -121,13 +115,12 @@
121115
{#if includeArea}
122116
<path d={areaFunction(dataArray)} fill={areaFillColor}></path>
123117
{/if}
124-
125118
<path
126119
d={lineFunction(dataArray)}
127120
fill="none"
128-
stroke="transparent"
129-
stroke-width="20"
130-
pointer-events="stroke"
121+
stroke="invisible"
122+
stroke-width={invisibleStrokeWidth}
123+
pointer-events={interactive ? "stroke" : "none"}
131124
></path>
132125
{#if halo}
133126
<path
@@ -136,6 +129,7 @@
136129
stroke={chartBackgroundColor}
137130
stroke-width={pathStrokeWidth * 1.2}
138131
stroke-dasharray={pathStrokeDashArray}
132+
pointer-events="none"
139133
></path>
140134
{/if}
141135
<path
@@ -144,6 +138,7 @@
144138
stroke={pathStrokeColor}
145139
stroke-width={pathStrokeWidth}
146140
stroke-dasharray={pathStrokeDashArray}
141+
pointer-events="none"
147142
></path>
148143
149144
<!-- {#if includeMarkers}

0 commit comments

Comments
 (0)