├── .gitignore
├── README.md
├── defs.d.ts
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
├── robots.txt
└── title.png
├── src
├── App.scss
├── App.tsx
├── components
│ ├── ChallengeTimer.scss
│ ├── ChallengeTimer.tsx
│ ├── Prompts.ts
│ ├── PromptsPanel.scss
│ └── PromptsPanel.tsx
├── index.css
├── index.tsx
├── logo.svg
├── react-app-env.d.ts
├── reportWebVitals.ts
├── setupTests.ts
└── utils
│ └── DateUtils.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This website is deployed to https://7daysofcode.art from the master branch.
2 |
3 | ## Prerequisites
4 | You should have NodeJS installed (which installs npm along with it)
5 | https://nodejs.org/en/
6 |
7 | ## Building and running
8 |
9 | To setup the repo (or after a git pull), run `npm install`.
10 |
11 | ### npm install
12 |
13 | In the project directory, you can run:
14 |
15 | ### `npm start`
16 |
17 | Runs the app in the development mode.\
18 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
19 |
20 | The page will reload if you make edits.\
21 | You will also see any lint errors in the console.
--------------------------------------------------------------------------------
/defs.d.ts:
--------------------------------------------------------------------------------
1 | declare module "tiny-relative-date" {
2 | var relativeDate: (d: Date) => any;
3 | export default relativeDate;
4 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "7daysofcode",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.12.0",
7 | "@testing-library/react": "^11.2.7",
8 | "@testing-library/user-event": "^12.8.3",
9 | "@types/jest": "^26.0.23",
10 | "@types/node": "^12.20.13",
11 | "@types/react": "^17.0.5",
12 | "@types/react-dom": "^17.0.5",
13 | "react": "^17.0.2",
14 | "react-countdown": "2.3.2",
15 | "react-dom": "^17.0.2",
16 | "react-scripts": "4.0.3",
17 | "sass": "1.34.0",
18 | "typescript": "^4.2.4",
19 | "web-vitals": "^1.1.2"
20 | },
21 | "scripts": {
22 | "start": "react-scripts start",
23 | "build": "react-scripts build",
24 | "test": "react-scripts test",
25 | "eject": "react-scripts eject"
26 | },
27 | "eslintConfig": {
28 | "extends": [
29 | "react-app",
30 | "react-app/jest"
31 | ]
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.2%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 1 chrome version",
41 | "last 1 firefox version",
42 | "last 1 safari version"
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SabinT/7daysofcode/50cb1005add861e91203e74f341ff79a3b55e854/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
14 |
15 |
19 |
20 |
29 | 7 Days of Code
30 |
31 |
32 | You need to enable JavaScript to run this app.
33 |
34 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SabinT/7daysofcode/50cb1005add861e91203e74f341ff79a3b55e854/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SabinT/7daysofcode/50cb1005add861e91203e74f341ff79a3b55e854/public/logo512.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SabinT/7daysofcode/50cb1005add861e91203e74f341ff79a3b55e854/public/title.png
--------------------------------------------------------------------------------
/src/App.scss:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #282c34;
3 | color: white;
4 | }
5 |
6 | .App {
7 | max-width: 800px;
8 | padding-left: 8px;
9 | padding-right: 8px;
10 | margin: auto;
11 | font-family: "Inconsolata", monospace;
12 | font-weight: 300;
13 | font-size: calc(8px + 2vmin);
14 |
15 | .App-title-image {
16 | width: 100%;
17 | margin-top: 8px;
18 | margin-bottom: 32px;
19 | }
20 |
21 | .App-temporary-notice {
22 | margin-bottom: 16px;
23 | }
24 |
25 | strong {
26 | font-weight: 400;
27 | background-color: black;
28 | }
29 |
30 | ol {
31 | list-style-type: decimal-leading-zero;
32 |
33 | li {
34 | padding-top: 10px;
35 | padding-bottom: 10px;
36 | }
37 | }
38 |
39 | .App-header {
40 | display: flex;
41 | flex-direction: column;
42 | text-align: center;
43 | align-items: center;
44 | justify-content: center;
45 | font-size: calc(10px + 2vmin);
46 | }
47 |
48 | .App-footer {
49 | font-size: medium;
50 | margin-top: 48px;
51 | }
52 |
53 | a {
54 | color: #afcaff;
55 | }
56 |
57 | a:hover {
58 | background-color: white;
59 | color: black;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import "./App.scss";
2 | import ChallengeTimer from "./components/ChallengeTimer";
3 | import PromptsPanel from "./components/PromptsPanel";
4 | // import PromptsPanel from "./components/PromptsPanel";
5 |
6 | function App() {
7 | return (
8 |
9 |
10 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
Some tips:
22 |
23 |
24 | There are several websites that will let you pick colors based on relationships between colors on a color wheel (example ).
25 | Even if you're picking colors with code, you can do something similar!
26 |
27 |
28 | You don't always have to work with RGB colors, you can use alternate representations like HSL.
29 | Consider reading about "color spaces" and "perceptual color models", as there are more than one way to blend colors, or to create gradients between them.
30 | Here's an interesting article by Raph Levien comparing different color spaces.
31 |
32 |
33 | Consider adding a book on color theory to your library. Some recommendations:
34 |
35 | The geometry of color perception
36 | Modern colour theory for traditional and digital painting media
37 | The Art of Color: Color Wheel & Color Relationships
38 | Color and Light: A guide for the realist painter (by James Gurney )
39 | The Art of Color: The Subjective Experience and Objective Rationale of Color (by Johannes Itten)
40 |
41 | Many thanks to these folks for above recommendations:
42 | Amy Goodchild ,
43 | Ben Kovach ,
44 | Louis-André Labadie ,
45 | Piter Pasma ,
46 | Ryan Struhl .
47 |
48 |
49 | Don't get stuck too much on theory, be sure to use your own intuition as well!
50 |
51 |
52 | The works of your favorite artists might make for good study material!
53 |
54 |
55 |
56 |
What is "7 Days of Code"?
57 |
58 | This is a super informal creative coding challenge that is meant to provide an instigation to create.
59 | No hard rules, no stressful deadlines. It's like an "art + code party" that you can join if you like the theme (and have the time for it).
60 |
61 |
62 |
Why should you participate?
63 |
64 |
65 | Get into the habit of creative coding and improve your workflow.
66 |
67 | Create alongside a community of like minded people
68 | Boost your portfolio with 7 new creations every month
69 | Practice and learn new algorithms and techniques
70 |
71 | Inspire and be inspired. Discover others' works, and be discovered.
72 |
73 |
74 |
75 |
How to participate:
76 |
77 |
78 | Use an element of programming to create art in any
79 | format you want (e.g., image, video, audio, electronics, LEDs,
80 | interactive, and whatnot). You can use any kind of programming: any
81 | language (including visual programming), any environment/framework.
82 |
83 |
84 | Make something with the prompt of the day in mind. You may
85 | re-interpret the prompt as you like (e.g., if the prompt is "gravity",
86 | you can also make something that has zero gravity)
87 |
88 |
89 | You may create something new, or iterate on something you were already
90 | working on.
91 |
92 |
93 | Share your work on social media with the tag{" "}
94 | #7daysofcode . (no need to share code)
95 |
96 |
97 | Follow the hashtag #7daysofcode (e.g., on{" "}
98 |
99 | Instagram
100 |
101 | ) to find works from other people on the challenge.
102 |
103 |
104 | If you cannot do all 7 days, pick the prompts you like and participate
105 | on the days you want!
106 |
107 |
108 |
109 |
Some tips:
110 |
111 |
112 | Beginner? Processing ,{" "}
113 | p5js , and{" "}
114 | openFrameworks are excellent
115 | general-purpose creative coding frameworks
116 |
117 |
118 | Make your work re-usable so that you can build upon it in the future
119 |
120 |
121 | Automate permutation of parameters/variables to discover hidden
122 | patterns
123 |
124 |
125 | Got suggestions? Send a message to the host , or file an issue on{" "}
126 | github .
127 |
128 |
129 |
130 | The code for this website is available on{" "}
131 |
github .
132 |
133 |
134 | );
135 | }
136 |
137 | export default App;
138 |
--------------------------------------------------------------------------------
/src/components/ChallengeTimer.scss:
--------------------------------------------------------------------------------
1 | .challenge-timer {
2 | border: 1px solid white;
3 | margin-top: 16px;
4 | margin-bottom: 16px;
5 | padding: 8px;
6 | font-size: calc(8px + 2vmin);
7 |
8 | p {
9 | padding: 0px;
10 | margin: 0px;
11 | }
12 |
13 | p + p {
14 | padding-top: 8px;
15 | }
16 | }
--------------------------------------------------------------------------------
/src/components/ChallengeTimer.tsx:
--------------------------------------------------------------------------------
1 | import './ChallengeTimer.scss';
2 | import Countdown, { CountdownRenderProps } from 'react-countdown';
3 | import * as Utils from '../utils/DateUtils';
4 |
5 | function timerRenderer(props: CountdownRenderProps) {
6 | if (props.completed) {
7 | // Render a completed state
8 | return The challenge is now active!
;
9 | } else {
10 | // Render a countdown
11 | return (
12 |
13 | {props.days} days,
14 | {props.hours} hours,
15 | {props.minutes} minutes,
16 | {props.seconds} seconds left.
17 |
18 | );
19 | }
20 | }
21 |
22 | function ChallengeTimer() {
23 | var today = new Date();
24 | if (Utils.isChallengeActive(today)) {
25 | // Don't show timer when a challenge is running
26 | return <>>;
27 | }
28 |
29 | const nextChallengeDate = Utils.getStartOfNextMonth(today);
30 |
31 | return (
32 |
33 |
Next challenge starts on {nextChallengeDate.toDateString()} .
34 |
35 |
39 |
40 |
41 | );
42 | }
43 |
44 | export default ChallengeTimer;
--------------------------------------------------------------------------------
/src/components/Prompts.ts:
--------------------------------------------------------------------------------
1 | export interface IPrompt {
2 | title: string;
3 | subtitle?: string;
4 | }
5 |
6 | // key: YYYY-MM-DD
7 | // value: prompt
8 | const prompts: { [key: string]: IPrompt } = {
9 | "2021-06-01": { title: "Sun" },
10 | "2021-06-02": { title: "Water" },
11 | "2021-06-03": { title: "Leaf" },
12 | "2021-06-04": { title: "Sky" },
13 | "2021-06-05": { title: "Flower" },
14 | "2021-06-06": { title: "Fire" },
15 | "2021-06-07": { title: "Wind" },
16 |
17 | "2021-07-01": { title: "Mirror" },
18 | "2021-07-02": { title: "Flow" },
19 | "2021-07-03": { title: "Repeat" },
20 | "2021-07-04": { title: "Combine" },
21 | "2021-07-05": { title: "Rotate" },
22 | "2021-07-06": { title: "Distort" },
23 | "2021-07-07": { title: "Divide" },
24 |
25 | "2021-08-01": { title: "Gravity", subtitle: "or no gravity" },
26 | "2021-08-02": { title: "Stars", subtitle: "and constellations" },
27 | "2021-08-03": { title: "Supernova", subtitle: "some kind of epic explosion" },
28 | "2021-08-04": { title: "Orbit", subtitle: "planets, moons, satellites, etc" },
29 | "2021-08-05": { title: "Nebula", subtitle: "space clouds where stars are born" },
30 | "2021-08-06": { title: "Galaxy", subtitle: "Milky Way, Andromeda, Whirlpool, etc" },
31 | "2021-08-07": { title: "Black Hole", subtitle: "" },
32 |
33 | "2021-10-01": {
34 | title: "Fixed Palette",
35 | subtitle: "Create a palette with at most 5 colors, and limit your sketch to use those colors."
36 | },
37 | "2021-10-02": { title: "Sourced from life", subtitle: "Pick colors based on something from real life. Could be your favorite flower, fruit, plant, animal, insect, photo, etc." },
38 | "2021-10-03": { title: "Retro", subtitle: "Something that evokes retro/historical vibes. For ideas, you could think about something you'd see on an old TV/video game/sci-fi movie, CRT monitors, old photographs/prints, etc." },
39 | "2021-10-04": { title: "Least favorite color", subtitle: "Instead of using the same favorite color all the time, maybe give a chance to the colors that you avoid ;)" },
40 | "2021-10-05": { title: "Gradient", subtitle: "Use a gradient between two or more colors. Multiple gradients OK." },
41 | "2021-10-06": { title: "Mild", subtitle: "Go for a mild color scheme, where colors aren't too saturated, and don't vary too much in contrast." },
42 | "2021-10-07": { title: "Vibrant", subtitle: "Use bright, vivid colors!" }
43 | };
44 |
45 | /**
46 | * key is the month in format "YYYY-MM"
47 | * value is the theme for the month
48 | */
49 | const themes: { [key: string]: string; } = {
50 | "2021-06": "Nature",
51 | "2021-07": "Transformations",
52 | "2021-08": "Space",
53 | "2021-10": "Colors"
54 | }
55 |
56 | function getLocalYearMonthDate(d: Date) {
57 | let month = "" + (d.getMonth() + 1);
58 | let day = "" + d.getDate();
59 | const year = d.getFullYear();
60 |
61 | if (month.length < 2) {
62 | month = "0" + month;
63 | }
64 |
65 | if (day.length < 2) {
66 | day = "0" + day;
67 | }
68 |
69 | return [year, month, day].join("-");
70 | }
71 |
72 | export function getPromptForDate(date: Date): IPrompt {
73 | const key = getLocalYearMonthDate(date);
74 | return prompts[key] ?? { title: "..."};
75 | }
76 |
77 | export function getThemeForDate(date: Date): string {
78 | const key = getLocalYearMonthDate(date).substr(0, 7);
79 | return themes[key] ?? "...";
80 | }
--------------------------------------------------------------------------------
/src/components/PromptsPanel.scss:
--------------------------------------------------------------------------------
1 | .prompts-theme {
2 | margin-bottom: 16px;
3 | }
4 |
5 | .prompts-list {
6 |
7 | .prompt {
8 | display: flex;
9 | flex-direction: row;
10 |
11 | .prompt-date {
12 | font-weight: 200;
13 | text-align: left;
14 | background-color: black;
15 | margin: 4px;
16 | padding: 16px;
17 | justify-content: center;
18 | display: flex;
19 | align-items: center;
20 | white-space: nowrap;
21 | }
22 |
23 | .prompt-text {
24 | background-color: black;
25 | margin: 4px;
26 | padding: 16px;
27 | text-align: left;
28 | display: flex;
29 | justify-content: center;
30 | flex-direction: column;
31 | width: 75%;
32 |
33 | .prompt-title {
34 | //margin-top: 8px;
35 | font-weight: 400;
36 | }
37 |
38 | .prompt-subtitle {
39 | margin-top: 4px;
40 | font-weight: 300;
41 | font-size: calc(6px + 2vmin);
42 | }
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/components/PromptsPanel.tsx:
--------------------------------------------------------------------------------
1 | import "./PromptsPanel.scss";
2 | import React from "react";
3 |
4 | import { getPromptForDate, getThemeForDate } from "./Prompts";
5 | import * as Utils from "../utils/DateUtils";
6 |
7 | const shortDateFormat = new Intl.DateTimeFormat("default", {
8 | month: "short",
9 | day: "numeric",
10 | });
11 | const yearMonthFormat = new Intl.DateTimeFormat("default", {
12 | month: "short",
13 | year: "numeric",
14 | });
15 |
16 | function PromptsPanel() {
17 | const today = new Date();
18 | //const today = new Date("2020-07-01Z");
19 |
20 | let challengeDate: Date;
21 | if (Utils.isChallengeAboutToStart(today)) {
22 | // Show prompts for next month towards the end of the month.
23 | challengeDate = Utils.getStartOfNextMonth(today);
24 | } else {
25 | challengeDate = Utils.getStartOfMonth(today);
26 | }
27 |
28 | const prompts: JSX.Element[] = [];
29 |
30 | for (let i = 0; i < 7; i++) {
31 | challengeDate.setDate(i + 1);
32 | const prompt = getPromptForDate(challengeDate);
33 |
34 | prompts.push(
35 |
36 |
37 | {shortDateFormat.format(challengeDate)}
38 |
39 |
40 |
{prompt.title}
41 |
{prompt.subtitle}
42 |
43 |
44 | );
45 | }
46 |
47 | return (
48 | <>
49 |
50 | Theme for {yearMonthFormat.format(challengeDate)}:{" "}
51 | {getThemeForDate(challengeDate)}
52 |
53 | {prompts}
54 | >
55 | );
56 | }
57 |
58 | export default PromptsPanel;
59 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/reportWebVitals.ts:
--------------------------------------------------------------------------------
1 | import { ReportHandler } from 'web-vitals';
2 |
3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => {
4 | if (onPerfEntry && onPerfEntry instanceof Function) {
5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
6 | getCLS(onPerfEntry);
7 | getFID(onPerfEntry);
8 | getFCP(onPerfEntry);
9 | getLCP(onPerfEntry);
10 | getTTFB(onPerfEntry);
11 | });
12 | }
13 | };
14 |
15 | export default reportWebVitals;
16 |
--------------------------------------------------------------------------------
/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/src/utils/DateUtils.ts:
--------------------------------------------------------------------------------
1 | export function getStartOfNextMonth(date: Date): Date {
2 | date.setDate(1);
3 | date.setMonth(date.getMonth() + 1);
4 | date.setHours(0, 0, 0, 0);
5 | return date;
6 | }
7 |
8 | export function getStartOfMonth(date: Date): Date {
9 | date.setDate(1);
10 | date.setHours(0, 0, 0, 0);
11 | return date;
12 | }
13 |
14 | export function isChallengeActive(date: Date): boolean {
15 | return date.getDate() <= 7;
16 | }
17 |
18 | export function isChallengeAboutToStart(date: Date): boolean {
19 | return date.getDate() > 21;
20 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src", "defs.d.ts"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------