Change prettier rules

This commit is contained in:
ErickSkrauch
2020-05-24 02:08:24 +03:00
parent 73f0c37a6a
commit f85b9d8d35
382 changed files with 24137 additions and 26046 deletions

View File

@@ -1,5 +1,5 @@
{
"criticalErrorHappened": "There was a critical error due to which the application can not continue its normal operation.",
"reloadPageOrContactUs": "Please reload this page and try again. If problem occurs again, please report it to the developers by sending email to",
"alsoYouCanInteractWithBackground": "You can also play around with the background it's interactable ;)"
"criticalErrorHappened": "There was a critical error due to which the application can not continue its normal operation.",
"reloadPageOrContactUs": "Please reload this page and try again. If problem occurs again, please report it to the developers by sending email to",
"alsoYouCanInteractWithBackground": "You can also play around with the background it's interactable ;)"
}

View File

@@ -9,48 +9,45 @@ import styles from './styles.scss';
import messages from './BSoD.intl.json';
interface State {
lastEventId?: string | void;
lastEventId?: string | void;
}
interface Props {
lastEventId?: string;
lastEventId?: string;
}
// TODO: probably it's better to render this view from the App view
// to remove dependencies from the store and IntlProvider
const BSoD: ComponentType<Props> = ({ lastEventId }) => {
let emailUrl = 'mailto:support@ely.by';
let emailUrl = 'mailto:support@ely.by';
if (lastEventId) {
emailUrl += `?subject=Bug report for #${lastEventId}`;
}
if (lastEventId) {
emailUrl += `?subject=Bug report for #${lastEventId}`;
}
return (
<div className={styles.body}>
<canvas
className={styles.canvas}
ref={(el: HTMLCanvasElement | null) => el && new BoxesField(el)}
/>
return (
<div className={styles.body}>
<canvas className={styles.canvas} ref={(el: HTMLCanvasElement | null) => el && new BoxesField(el)} />
<div className={styles.wrapper}>
<div className={styles.title}>
<Message {...appInfo.appName} />
<div className={styles.wrapper}>
<div className={styles.title}>
<Message {...appInfo.appName} />
</div>
<div className={styles.lineWithMargin}>
<Message {...messages.criticalErrorHappened} />
</div>
<div className={styles.line}>
<Message {...messages.reloadPageOrContactUs} />
</div>
<a href={emailUrl} className={styles.support}>
support@ely.by
</a>
<div className={styles.easterEgg}>
<Message {...messages.alsoYouCanInteractWithBackground} />
</div>
</div>
</div>
<div className={styles.lineWithMargin}>
<Message {...messages.criticalErrorHappened} />
</div>
<div className={styles.line}>
<Message {...messages.reloadPageOrContactUs} />
</div>
<a href={emailUrl} className={styles.support}>
support@ely.by
</a>
<div className={styles.easterEgg}>
<Message {...messages.alsoYouCanInteractWithBackground} />
</div>
</div>
</div>
);
);
};
export default BSoD;

View File

@@ -5,25 +5,25 @@ import logger from 'app/services/logger/logger';
import BSoD from './BSoD';
const BSoDContainer: ComponentType = () => {
const [lastEventId, setLastEventId] = useState<string>();
useEffect(() => {
const timer = setInterval(() => {
// eslint-disable-next-line no-shadow
const lastEventId = logger.getLastEventId();
const [lastEventId, setLastEventId] = useState<string>();
useEffect(() => {
const timer = setInterval(() => {
// eslint-disable-next-line no-shadow
const lastEventId = logger.getLastEventId();
if (!lastEventId) {
return;
}
if (!lastEventId) {
return;
}
clearInterval(timer);
setLastEventId(lastEventId);
}, 500);
clearInterval(timer);
setLastEventId(lastEventId);
}, 500);
// Don't care about interval cleanup because there is no way from
// BSoD state and page can be only reloaded
}, []);
// Don't care about interval cleanup because there is no way from
// BSoD state and page can be only reloaded
}, []);
return <BSoD lastEventId={lastEventId} />;
return <BSoD lastEventId={lastEventId} />;
};
export default BSoDContainer;

View File

@@ -3,115 +3,109 @@ import Point from './Point';
const shadowLength = 2000; // TODO: should be calculated
export default class Box {
public position: Point;
private angle: number;
public color: string;
private readonly shadowColor: string;
public position: Point;
private angle: number;
public color: string;
private readonly shadowColor: string;
private _size: number;
private _halfSize: number;
private _size: number;
private _halfSize: number;
constructor(
size: number,
position: Point,
startRotate: number,
color: string,
shadowColor: string,
) {
this.size = size;
this.position = position;
this.color = color;
this.angle = startRotate;
this.shadowColor = shadowColor;
}
public get size(): number {
return this._size;
}
public set size(size: number) {
this._size = size;
this._halfSize = Math.floor(size / 2);
}
public get halfSize(): number {
return this._halfSize;
}
get points(): { p1: Point; p2: Point; p3: Point; p4: Point } {
const full = (Math.PI * 2) / 4;
const p1: Point = {
x: this.position.x + this._halfSize * Math.sin(this.angle),
y: this.position.y + this._halfSize * Math.cos(this.angle),
};
const p2: Point = {
x: this.position.x + this._halfSize * Math.sin(this.angle + full),
y: this.position.y + this._halfSize * Math.cos(this.angle + full),
};
const p3: Point = {
x: this.position.x + this._halfSize * Math.sin(this.angle + full * 2),
y: this.position.y + this._halfSize * Math.cos(this.angle + full * 2),
};
const p4: Point = {
x: this.position.x + this._halfSize * Math.sin(this.angle + full * 3),
y: this.position.y + this._halfSize * Math.cos(this.angle + full * 3),
};
return { p1, p2, p3, p4 };
}
rotate(): void {
const speed = (60 - this._halfSize) / 20;
this.angle += speed * 0.002;
this.position.x += speed;
this.position.y += speed;
}
draw(ctx: CanvasRenderingContext2D): void {
const { points } = this;
ctx.beginPath();
ctx.moveTo(points.p1.x, points.p1.y);
ctx.lineTo(points.p2.x, points.p2.y);
ctx.lineTo(points.p3.x, points.p3.y);
ctx.lineTo(points.p4.x, points.p4.y);
ctx.fillStyle = this.color;
ctx.fill();
}
drawShadow(ctx: CanvasRenderingContext2D, light: Point): void {
const boxPoints = this.points;
const points: Array<{
startX: number;
startY: number;
endX: number;
endY: number;
}> = [];
Object.values(boxPoints).forEach((point) => {
const angle = Math.atan2(light.y - point.y, light.x - point.x);
const endX = point.x + shadowLength * Math.sin(-angle - Math.PI / 2);
const endY = point.y + shadowLength * Math.cos(-angle - Math.PI / 2);
points.push({
startX: point.x,
startY: point.y,
endX,
endY,
});
});
for (let i = points.length - 1; i >= 0; i--) {
const n = i === 3 ? 0 : i + 1;
ctx.beginPath();
ctx.moveTo(points[i].startX, points[i].startY);
ctx.lineTo(points[n].startX, points[n].startY);
ctx.lineTo(points[n].endX, points[n].endY);
ctx.lineTo(points[i].endX, points[i].endY);
ctx.fillStyle = this.shadowColor;
ctx.fill();
constructor(size: number, position: Point, startRotate: number, color: string, shadowColor: string) {
this.size = size;
this.position = position;
this.color = color;
this.angle = startRotate;
this.shadowColor = shadowColor;
}
public get size(): number {
return this._size;
}
public set size(size: number) {
this._size = size;
this._halfSize = Math.floor(size / 2);
}
public get halfSize(): number {
return this._halfSize;
}
get points(): { p1: Point; p2: Point; p3: Point; p4: Point } {
const full = (Math.PI * 2) / 4;
const p1: Point = {
x: this.position.x + this._halfSize * Math.sin(this.angle),
y: this.position.y + this._halfSize * Math.cos(this.angle),
};
const p2: Point = {
x: this.position.x + this._halfSize * Math.sin(this.angle + full),
y: this.position.y + this._halfSize * Math.cos(this.angle + full),
};
const p3: Point = {
x: this.position.x + this._halfSize * Math.sin(this.angle + full * 2),
y: this.position.y + this._halfSize * Math.cos(this.angle + full * 2),
};
const p4: Point = {
x: this.position.x + this._halfSize * Math.sin(this.angle + full * 3),
y: this.position.y + this._halfSize * Math.cos(this.angle + full * 3),
};
return { p1, p2, p3, p4 };
}
rotate(): void {
const speed = (60 - this._halfSize) / 20;
this.angle += speed * 0.002;
this.position.x += speed;
this.position.y += speed;
}
draw(ctx: CanvasRenderingContext2D): void {
const { points } = this;
ctx.beginPath();
ctx.moveTo(points.p1.x, points.p1.y);
ctx.lineTo(points.p2.x, points.p2.y);
ctx.lineTo(points.p3.x, points.p3.y);
ctx.lineTo(points.p4.x, points.p4.y);
ctx.fillStyle = this.color;
ctx.fill();
}
drawShadow(ctx: CanvasRenderingContext2D, light: Point): void {
const boxPoints = this.points;
const points: Array<{
startX: number;
startY: number;
endX: number;
endY: number;
}> = [];
Object.values(boxPoints).forEach((point) => {
const angle = Math.atan2(light.y - point.y, light.x - point.x);
const endX = point.x + shadowLength * Math.sin(-angle - Math.PI / 2);
const endY = point.y + shadowLength * Math.cos(-angle - Math.PI / 2);
points.push({
startX: point.x,
startY: point.y,
endX,
endY,
});
});
for (let i = points.length - 1; i >= 0; i--) {
const n = i === 3 ? 0 : i + 1;
ctx.beginPath();
ctx.moveTo(points[i].startX, points[i].startY);
ctx.lineTo(points[n].startX, points[n].startY);
ctx.lineTo(points[n].endX, points[n].endY);
ctx.lineTo(points[i].endX, points[i].endY);
ctx.fillStyle = this.shadowColor;
ctx.fill();
}
}
}
}

View File

@@ -2,177 +2,157 @@ import Point from './Point';
import Box from './Box';
interface Params {
countBoxes: number;
boxMinSize: number;
boxMaxSize: number;
backgroundColor: string;
lightColor: string;
shadowColor: string;
boxColors: ReadonlyArray<string>;
countBoxes: number;
boxMinSize: number;
boxMaxSize: number;
backgroundColor: string;
lightColor: string;
shadowColor: string;
boxColors: ReadonlyArray<string>;
}
/**
* Based on http://codepen.io/mladen___/pen/gbvqBo
*/
export default class BoxesField {
private readonly elem: HTMLCanvasElement;
private readonly ctx: CanvasRenderingContext2D;
private readonly params: Params;
private readonly elem: HTMLCanvasElement;
private readonly ctx: CanvasRenderingContext2D;
private readonly params: Params;
private light: Point;
private boxes: Array<Box>;
private light: Point;
private boxes: Array<Box>;
/**
* @param {HTMLCanvasElement} elem - canvas DOM node
* @param {object} params
*/
constructor(
elem: HTMLCanvasElement,
params: Params = {
countBoxes: 14,
boxMinSize: 20,
boxMaxSize: 75,
backgroundColor: '#233d49',
lightColor: '#28555b',
shadowColor: '#274451',
boxColors: [
'#207e5c',
'#5b9aa9',
'#e66c69',
'#6b5b8c',
'#8b5d79',
'#dd8650',
],
},
) {
this.elem = elem;
const ctx = elem.getContext('2d');
/**
* @param {HTMLCanvasElement} elem - canvas DOM node
* @param {object} params
*/
constructor(
elem: HTMLCanvasElement,
params: Params = {
countBoxes: 14,
boxMinSize: 20,
boxMaxSize: 75,
backgroundColor: '#233d49',
lightColor: '#28555b',
shadowColor: '#274451',
boxColors: ['#207e5c', '#5b9aa9', '#e66c69', '#6b5b8c', '#8b5d79', '#dd8650'],
},
) {
this.elem = elem;
const ctx = elem.getContext('2d');
if (!ctx) {
throw new Error('Cannot get canvas 2d context');
if (!ctx) {
throw new Error('Cannot get canvas 2d context');
}
this.ctx = ctx;
this.params = params;
this.light = {
x: 160,
y: 200,
};
this.resize();
this.drawLoop();
this.bindWindowListeners();
this.boxes = [];
while (this.boxes.length < this.params.countBoxes) {
this.boxes.push(
new Box(
Math.floor(
Math.random() * (this.params.boxMaxSize - this.params.boxMinSize) + this.params.boxMinSize,
),
{
x: Math.floor(Math.random() * elem.width + 1),
y: Math.floor(Math.random() * elem.height + 1),
},
Math.random() * Math.PI,
this.getRandomColor(),
this.params.shadowColor,
),
);
}
}
this.ctx = ctx;
this.params = params;
this.light = {
x: 160,
y: 200,
};
this.resize();
this.drawLoop();
this.bindWindowListeners();
this.boxes = [];
while (this.boxes.length < this.params.countBoxes) {
this.boxes.push(
new Box(
Math.floor(
Math.random() * (this.params.boxMaxSize - this.params.boxMinSize) +
this.params.boxMinSize,
),
{
x: Math.floor(Math.random() * elem.width + 1),
y: Math.floor(Math.random() * elem.height + 1),
},
Math.random() * Math.PI,
this.getRandomColor(),
this.params.shadowColor,
),
);
}
}
resize(): void {
const { width, height } = this.elem.getBoundingClientRect();
this.elem.width = width;
this.elem.height = height;
}
drawLight(light: Point): void {
const greaterSize =
window.screen.width > window.screen.height
? window.screen.width
: window.screen.height;
// еее, теорема пифагора и описывание окружности вокруг квадрата, не зря в универ ходил!!!
const lightRadius = greaterSize * Math.sqrt(2);
this.ctx.beginPath();
this.ctx.arc(light.x, light.y, lightRadius, 0, 2 * Math.PI);
const gradient = this.ctx.createRadialGradient(
light.x,
light.y,
0,
light.x,
light.y,
lightRadius,
);
gradient.addColorStop(0, this.params.lightColor);
gradient.addColorStop(1, this.params.backgroundColor);
this.ctx.fillStyle = gradient;
this.ctx.fill();
}
drawLoop(): void {
this.ctx.clearRect(0, 0, this.elem.width, this.elem.height);
this.drawLight(this.light);
for (const i in this.boxes) {
if (!this.boxes.hasOwnProperty(i)) {
continue;
}
const box = this.boxes[i];
box.rotate();
box.drawShadow(this.ctx, this.light);
resize(): void {
const { width, height } = this.elem.getBoundingClientRect();
this.elem.width = width;
this.elem.height = height;
}
for (const i in this.boxes) {
if (!this.boxes.hasOwnProperty(i)) {
continue;
}
drawLight(light: Point): void {
const greaterSize = window.screen.width > window.screen.height ? window.screen.width : window.screen.height;
// еее, теорема пифагора и описывание окружности вокруг квадрата, не зря в универ ходил!!!
const lightRadius = greaterSize * Math.sqrt(2);
const box = this.boxes[i];
box.draw(this.ctx);
// When box leaves window boundaries
let shouldUpdateBox = false;
if (box.position.y - box.halfSize > this.elem.height) {
box.position.y -= this.elem.height + 100;
shouldUpdateBox = true;
}
if (box.position.x - box.halfSize > this.elem.width) {
box.position.x -= this.elem.width + 100;
shouldUpdateBox = true;
}
if (shouldUpdateBox) {
this.updateBox(box);
}
this.ctx.beginPath();
this.ctx.arc(light.x, light.y, lightRadius, 0, 2 * Math.PI);
const gradient = this.ctx.createRadialGradient(light.x, light.y, 0, light.x, light.y, lightRadius);
gradient.addColorStop(0, this.params.lightColor);
gradient.addColorStop(1, this.params.backgroundColor);
this.ctx.fillStyle = gradient;
this.ctx.fill();
}
requestAnimationFrame(this.drawLoop.bind(this));
}
drawLoop(): void {
this.ctx.clearRect(0, 0, this.elem.width, this.elem.height);
this.drawLight(this.light);
bindWindowListeners() {
window.addEventListener('resize', this.resize.bind(this));
window.addEventListener('mousemove', (event) => {
this.light.x = event.clientX;
this.light.y = event.clientY;
});
}
for (const i in this.boxes) {
if (!this.boxes.hasOwnProperty(i)) {
continue;
}
updateBox(box: Box): void {
box.color = this.getRandomColor();
}
const box = this.boxes[i];
box.rotate();
box.drawShadow(this.ctx, this.light);
}
getRandomColor(): string {
return this.params.boxColors[
Math.floor(Math.random() * this.params.boxColors.length)
];
}
for (const i in this.boxes) {
if (!this.boxes.hasOwnProperty(i)) {
continue;
}
const box = this.boxes[i];
box.draw(this.ctx);
// When box leaves window boundaries
let shouldUpdateBox = false;
if (box.position.y - box.halfSize > this.elem.height) {
box.position.y -= this.elem.height + 100;
shouldUpdateBox = true;
}
if (box.position.x - box.halfSize > this.elem.width) {
box.position.x -= this.elem.width + 100;
shouldUpdateBox = true;
}
if (shouldUpdateBox) {
this.updateBox(box);
}
}
requestAnimationFrame(this.drawLoop.bind(this));
}
bindWindowListeners() {
window.addEventListener('resize', this.resize.bind(this));
window.addEventListener('mousemove', (event) => {
this.light.x = event.clientX;
this.light.y = event.clientY;
});
}
updateBox(box: Box): void {
box.color = this.getRandomColor();
}
getRandomColor(): string {
return this.params.boxColors[Math.floor(Math.random() * this.params.boxColors.length)];
}
}

View File

@@ -4,46 +4,39 @@ import sinon from 'sinon';
import BsodMiddleware from 'app/components/ui/bsod/BsodMiddleware';
describe('BsodMiddleware', () => {
[500, 503, 555].forEach((code) =>
it(`should dispatch for ${code}`, () => {
const resp = {
originalResponse: { status: code },
};
[500, 503, 555].forEach((code) =>
it(`should dispatch for ${code}`, () => {
const resp = {
originalResponse: { status: code },
};
const dispatchBsod = sinon.spy();
const logger = { warn: sinon.spy() };
const dispatchBsod = sinon.spy();
const logger = { warn: sinon.spy() };
const middleware = new BsodMiddleware(dispatchBsod, logger as any);
const middleware = new BsodMiddleware(dispatchBsod, logger as any);
return expect(middleware.catch(resp), 'to be rejected with', resp).then(
() => {
expect(dispatchBsod, 'was called');
expect(logger.warn, 'to have a call satisfying', [
'Unexpected response (BSoD)',
{ resp },
]);
},
);
}),
);
return expect(middleware.catch(resp), 'to be rejected with', resp).then(() => {
expect(dispatchBsod, 'was called');
expect(logger.warn, 'to have a call satisfying', ['Unexpected response (BSoD)', { resp }]);
});
}),
);
[200, 404].forEach((code) =>
it(`should not dispatch for ${code}`, () => {
const resp = {
originalResponse: { status: code },
};
[200, 404].forEach((code) =>
it(`should not dispatch for ${code}`, () => {
const resp = {
originalResponse: { status: code },
};
const dispatchBsod = sinon.spy();
const logger = { warn: sinon.spy() };
const dispatchBsod = sinon.spy();
const logger = { warn: sinon.spy() };
const middleware = new BsodMiddleware(dispatchBsod, logger as any);
const middleware = new BsodMiddleware(dispatchBsod, logger as any);
return expect(middleware.catch(resp), 'to be rejected with', resp).then(
() => {
expect(dispatchBsod, 'was not called');
expect(logger.warn, 'was not called');
},
);
}),
);
return expect(middleware.catch(resp), 'to be rejected with', resp).then(() => {
expect(dispatchBsod, 'was not called');
expect(logger.warn, 'was not called');
});
}),
);
});

View File

@@ -7,42 +7,39 @@ type Logger = typeof defaultLogger;
const ABORT_ERR = 20;
class BsodMiddleware implements Middleware {
dispatchBsod: () => any;
logger: Logger;
dispatchBsod: () => any;
logger: Logger;
constructor(dispatchBsod: () => any, logger: Logger = defaultLogger) {
this.dispatchBsod = dispatchBsod;
this.logger = logger;
}
async catch<T extends Resp<any>>(
resp?: T | InternalServerError | Error,
): Promise<T> {
const { originalResponse } = (resp || {}) as InternalServerError;
if (
resp &&
((resp instanceof InternalServerError &&
(resp.error as any).code !== ABORT_ERR) ||
(originalResponse && /5\d\d/.test(originalResponse.status)))
) {
this.dispatchBsod();
const { message: errorMessage } = resp as { [key: string]: any };
if (!errorMessage || !/NetworkError/.test(errorMessage)) {
let message = 'Unexpected response (BSoD)';
if (errorMessage) {
message = `BSoD: ${errorMessage}`;
}
this.logger.warn(message, { resp });
}
constructor(dispatchBsod: () => any, logger: Logger = defaultLogger) {
this.dispatchBsod = dispatchBsod;
this.logger = logger;
}
return Promise.reject(resp);
}
async catch<T extends Resp<any>>(resp?: T | InternalServerError | Error): Promise<T> {
const { originalResponse } = (resp || {}) as InternalServerError;
if (
resp &&
((resp instanceof InternalServerError && (resp.error as any).code !== ABORT_ERR) ||
(originalResponse && /5\d\d/.test(originalResponse.status)))
) {
this.dispatchBsod();
const { message: errorMessage } = resp as { [key: string]: any };
if (!errorMessage || !/NetworkError/.test(errorMessage)) {
let message = 'Unexpected response (BSoD)';
if (errorMessage) {
message = `BSoD: ${errorMessage}`;
}
this.logger.warn(message, { resp });
}
}
return Promise.reject(resp);
}
}
export default BsodMiddleware;

View File

@@ -1,4 +1,4 @@
export default interface Point {
x: number;
y: number;
x: number;
y: number;
}

View File

@@ -1,13 +1,13 @@
import { Action as ReduxAction } from 'redux';
interface BSoDAction extends ReduxAction {
type: 'BSOD';
type: 'BSOD';
}
export function bsod(): BSoDAction {
return {
type: 'BSOD',
};
return {
type: 'BSOD',
};
}
export type Action = BSoDAction;

View File

@@ -11,31 +11,28 @@ let injectedStore: Store;
let injectedHistory: History<any>;
let onBsod: undefined | (() => void);
export default function dispatchBsod(
store = injectedStore,
history = injectedHistory,
) {
store.dispatch(bsod());
onBsod && onBsod();
export default function dispatchBsod(store = injectedStore, history = injectedHistory) {
store.dispatch(bsod());
onBsod && onBsod();
ReactDOM.render(
<ContextProvider store={store} history={history}>
<BSoDContainer />
</ContextProvider>,
document.getElementById('app'),
);
ReactDOM.render(
<ContextProvider store={store} history={history}>
<BSoDContainer />
</ContextProvider>,
document.getElementById('app'),
);
}
export function inject({
store,
history,
stopLoading,
store,
history,
stopLoading,
}: {
store: Store;
history: History<any>;
stopLoading: () => void;
store: Store;
history: History<any>;
stopLoading: () => void;
}) {
injectedStore = store;
injectedHistory = history;
onBsod = stopLoading;
injectedStore = store;
injectedHistory = history;
onBsod = stopLoading;
}

View File

@@ -7,16 +7,16 @@ import dispatchBsod, { inject } from './dispatchBsod';
import BsodMiddleware from './BsodMiddleware';
export default function factory({
store,
history,
stopLoading,
store,
history,
stopLoading,
}: {
store: Store;
history: History<any>;
stopLoading: () => void;
store: Store;
history: History<any>;
stopLoading: () => void;
}) {
inject({ store, history, stopLoading });
inject({ store, history, stopLoading });
// do bsod for 500/404 errors
request.addMiddleware(new BsodMiddleware(dispatchBsod, logger));
// do bsod for 500/404 errors
request.addMiddleware(new BsodMiddleware(dispatchBsod, logger));
}

View File

@@ -3,9 +3,9 @@ import { Action } from './actions';
export type State = boolean;
export default function (state: State = false, { type }: Action): State {
if (type === 'BSOD') {
return true;
}
if (type === 'BSOD') {
return true;
}
return state;
return state;
}

View File

@@ -1,64 +1,63 @@
@import '~app/components/ui/colors.scss';
$font-family-monospaced: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Roboto Mono',
monospace;
$font-family-monospaced: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Roboto Mono', monospace;
.body {
height: 100%;
background-color: $dark_blue;
color: #fff;
text-align: center;
font-family: $font-family-monospaced;
box-sizing: border-box;
height: 100%;
background-color: $dark_blue;
color: #fff;
text-align: center;
font-family: $font-family-monospaced;
box-sizing: border-box;
}
.canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.wrapper {
position: relative;
margin: 85px auto 0;
max-width: 500px;
padding: 0 20px;
position: relative;
margin: 85px auto 0;
max-width: 500px;
padding: 0 20px;
}
.title {
font-size: 26px;
margin-bottom: 13px;
font-size: 26px;
margin-bottom: 13px;
}
.line {
margin: 0 auto;
font-size: 16px;
color: #ebe8e1;
margin: 0 auto;
font-size: 16px;
color: #ebe8e1;
}
.lineWithMargin {
composes: line;
composes: line;
margin-bottom: 20px;
margin-bottom: 20px;
}
.support {
font-size: 18px;
line-height: 18px;
color: #fff;
margin: 5px 0 44px;
display: inline-block;
border-bottom-color: #39777f;
&:hover {
font-size: 18px;
line-height: 18px;
color: #fff;
border-bottom-color: #eee;
}
margin: 5px 0 44px;
display: inline-block;
border-bottom-color: #39777f;
&:hover {
color: #fff;
border-bottom-color: #eee;
}
}
.easterEgg {
font-size: 14px;
color: #ebe8e1;
font-size: 14px;
color: #ebe8e1;
}