├── README.md
├── css
├── popup.css
└── style.css
├── js
├── background.js
├── content.js
└── popup.js
├── manifest.json
└── popup.html
/README.md:
--------------------------------------------------------------------------------
1 | # Twice - Think Twice Before Social Media
2 |
3 | A Chrome extension that helps you be more mindful of your social media usage by prompting you to think twice before visiting social media sites. It's designed to reduce mindless scrolling and promote intentional browsing habits.
4 |
5 |
6 |
7 |
8 |
9 | ## Features
10 |
11 | - Displays a gentle reminder when you attempt to visit social media sites
12 | - Helps you pause and reflect on whether you really need to check social media
13 | - Promotes mindful internet usage and better digital habits
14 |
15 | ## Installation
16 |
17 | 1. Clone this repository or download the source code
18 | 2. Open Chrome and navigate to `chrome://extensions/`
19 | 3. Enable "Developer mode" by toggling the switch in the top right corner
20 | 4. Click "Load unpacked" button
21 | 5. Select the directory containing the extension files (where manifest.json is located)
22 |
23 | The extension icon should now appear in your Chrome toolbar.
24 |
25 | ## How It Works
26 |
27 | When you try to visit a social media site, Twice will show a popup asking you to pause and reflect. This small interruption helps you:
28 | - Be more conscious of your browsing habits
29 | - Reduce impulsive social media checking
30 | - Make intentional decisions about your online time
31 |
32 | ## Development
33 |
34 | The extension consists of:
35 | - `manifest.json`: Extension configuration
36 | - `popup.html`: Main extension interface
37 | - `css/`: Styling files
38 | - `js/`: JavaScript functionality
39 |
40 | To make changes, simply edit the files and reload the extension in Chrome by clicking the refresh icon on the extension card in `chrome://extensions/`.
41 |
42 | ## License
43 |
44 | MIT
45 |
--------------------------------------------------------------------------------
/css/popup.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500&display=swap');
2 |
3 | body {
4 | width: 280px;
5 | padding: 16px;
6 | margin: 0;
7 | font-family: 'Inter', -apple-system, sans-serif;
8 | font-size: 13px;
9 | color: #fff;
10 | background-color: #1C1C1E;
11 | }
12 |
13 | h2 {
14 | font-size: 14px;
15 | font-weight: 500;
16 | margin: 0 0 12px 0;
17 | letter-spacing: -0.01em;
18 | color: #fff;
19 | }
20 |
21 | h3 {
22 | font-size: 12px;
23 | font-weight: 500;
24 | margin: 0 0 8px 0;
25 | letter-spacing: -0.01em;
26 | color: #999;
27 | }
28 |
29 | .site-list {
30 | margin: 0;
31 | padding: 0;
32 | list-style: none;
33 | max-height: 200px;
34 | overflow-y: auto;
35 | }
36 |
37 | .site-item {
38 | position: relative;
39 | display: flex;
40 | align-items: center;
41 | justify-content: space-between;
42 | padding: 4px;
43 | padding-right: 8px;
44 | }
45 |
46 | .site-item .domain-text {
47 | flex-grow: 1;
48 | }
49 |
50 | .site-item .controls {
51 | display: flex;
52 | align-items: center;
53 | gap: 8px;
54 | }
55 |
56 | .delete-btn {
57 | background: transparent;
58 | border: none;
59 | color: #ff3b30;
60 | cursor: pointer;
61 | font-size: 14px;
62 | font-weight: 400;
63 | line-height: 1;
64 | padding: 4px 8px;
65 | border-radius: 4px;
66 | transition: all 0.2s ease;
67 | }
68 |
69 | .delete-btn:hover {
70 | background: rgba(255, 59, 48, 0.1);
71 | }
72 |
73 | .site-item .toggle {
74 | margin-left: 8px;
75 | }
76 |
77 | .site-item:last-child {
78 | border-bottom: none;
79 | }
80 |
81 | .toggle {
82 | position: relative;
83 | display: inline-block;
84 | width: 32px;
85 | height: 18px;
86 | }
87 |
88 | .toggle input {
89 | opacity: 0;
90 | width: 0;
91 | height: 0;
92 | }
93 |
94 | .slider {
95 | position: absolute;
96 | cursor: pointer;
97 | top: 0;
98 | left: 0;
99 | right: 0;
100 | bottom: 0;
101 | background-color: #3A3A3C;
102 | transition: .2s;
103 | border-radius: 34px;
104 | }
105 |
106 | .slider:before {
107 | position: absolute;
108 | content: "";
109 | height: 14px;
110 | width: 14px;
111 | left: 2px;
112 | bottom: 2px;
113 | background-color: #fff;
114 | transition: .2s;
115 | border-radius: 50%;
116 | }
117 |
118 | input:checked + .slider {
119 | background-color: #0A84FF;
120 | }
121 |
122 | input:checked + .slider:before {
123 | transform: translateX(14px);
124 | }
125 |
126 | .current-site {
127 | margin-top: 20px;
128 | padding-top: 16px;
129 | border-top: 1px solid rgba(255, 255, 255, 0.1);
130 | }
131 |
132 | .current-site p {
133 | margin: 0 0 8px 0;
134 | color: #999;
135 | font-size: 12px;
136 | }
137 |
138 | .add-button {
139 | display: inline-block;
140 | width: 100%;
141 | padding: 8px 16px;
142 | background-color: #2C2C2E;
143 | color: #fff;
144 | border: 1px solid rgba(255, 255, 255, 0.2);
145 | border-radius: 4px;
146 | cursor: pointer;
147 | text-align: center;
148 | font-size: 13px;
149 | transition: background-color 0.2s;
150 | }
151 |
152 | .add-button:hover {
153 | background-color: #3A3A3C;
154 | }
155 |
156 | .add-button:disabled {
157 | background-color: #eee;
158 | color: #999;
159 | cursor: not-allowed;
160 | }
161 |
162 | .status-message {
163 | margin-top: 8px;
164 | font-size: 12px;
165 | color: #666;
166 | text-align: center;
167 | min-height: 16px;
168 | }
169 |
--------------------------------------------------------------------------------
/css/style.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500&display=swap");
2 |
3 | .focus-reminder {
4 | all: initial !important;
5 | position: fixed !important;
6 | display: flex !important;
7 | align-items: center !important;
8 | justify-content: center !important;
9 | top: 0% !important;
10 | left: 0% !important;
11 | background: rgba(28, 28, 30, 0.98) !important;
12 | padding: 32px !important;
13 | z-index: 2147483647 !important;
14 | width: 100vw !important;
15 | height: 100vh !important;
16 | font-family: "Inter", -apple-system, sans-serif !important;
17 | box-sizing: border-box !important;
18 | }
19 |
20 | .focus-reminder * {
21 | all: revert !important;
22 | font-family: "Inter", -apple-system, sans-serif !important;
23 | box-sizing: border-box !important;
24 | line-height: normal !important;
25 | }
26 |
27 | .focus-reminder-content {
28 | max-width: 400px !important;
29 | text-align: center !important;
30 | background: rgba(44, 44, 46, 0.5) !important;
31 | padding: 32px !important;
32 | border-radius: 16px !important;
33 | border: 1px solid rgba(255, 255, 255, 0.1) !important;
34 | backdrop-filter: blur(20px) !important;
35 | }
36 |
37 | .focus-reminder h2 {
38 | margin: 0 0 16px 0 !important;
39 | color: #ffffff !important;
40 | font-size: 24px !important;
41 | font-weight: 500 !important;
42 | letter-spacing: -0.01em !important;
43 | }
44 |
45 | .focus-reminder p {
46 | color: #999 !important;
47 | font-size: 14px !important;
48 | line-height: 1.5 !important;
49 | margin: 0 0 8px 0 !important;
50 | font-weight: 300 !important;
51 | }
52 |
53 | .focus-reminder .section {
54 | margin: 24px 0 !important;
55 | padding: 16px !important;
56 | border-radius: 12px !important;
57 | background: rgba(255, 255, 255, 0.05) !important;
58 | }
59 |
60 | .focus-reminder .section-title {
61 | color: #fff !important;
62 | font-size: 16px !important;
63 | font-weight: 500 !important;
64 | margin-bottom: 8px !important;
65 | letter-spacing: -0.01em !important;
66 | }
67 |
68 | .focus-reminder .section-subtitle {
69 | color: #999 !important;
70 | font-size: 14px !important;
71 | font-weight: 300 !important;
72 | margin-bottom: 16px !important;
73 | line-height: 1.6 !important;
74 | }
75 |
76 | .focus-reminder .timer-options {
77 | margin: 0 auto !important;
78 | max-width: 280px !important;
79 | }
80 |
81 | .focus-reminder .schedule {
82 | background-color: rgba(255, 255, 255, 0.1) !important;
83 | color: white !important;
84 | border: 1px solid rgba(255, 255, 255, 0.2) !important;
85 | padding: 12px 24px !important;
86 | font-size: 14px !important;
87 | font-weight: 400 !important;
88 | border-radius: 8px !important;
89 | cursor: pointer !important;
90 | transition: all 0.2s ease !important;
91 | width: 100% !important;
92 | }
93 |
94 | .focus-reminder .schedule:hover {
95 | background-color: rgba(255, 255, 255, 0.15) !important;
96 | transform: translateY(-1px) !important;
97 | }
98 |
99 | .focus-reminder .countdown {
100 | font-size: 32px !important;
101 | font-weight: 500 !important;
102 | color: white !important;
103 | margin: 16px 0 !important;
104 | }
105 |
106 | .focus-reminder-buttons {
107 | margin-top: 32px !important;
108 | text-align: center !important;
109 | }
110 |
111 | @keyframes breathing {
112 | 0% {
113 | transform: scale(1) !important;
114 | }
115 | 50% {
116 | transform: scale(1.02) !important;
117 | }
118 | 100% {
119 | transform: scale(1) !important;
120 | }
121 | }
122 |
123 | .focus-reminder .leave {
124 | background-color: #2563eb !important;
125 | color: white !important;
126 | border: none !important;
127 | padding: 12px 24px !important;
128 | font-size: 16px !important;
129 | font-weight: 500 !important;
130 | border-radius: 8px !important;
131 | cursor: pointer !important;
132 | transition: all 0.2s ease !important;
133 | margin-bottom: 8px !important;
134 | animation: breathing 2s ease-in-out infinite !important;
135 | }
136 |
137 | .focus-reminder .leave:hover {
138 | background-color: #1d4ed8 !important;
139 | transform: translateY(-1px) scale(1.02) !important;
140 | box-shadow: 0 4px 12px rgba(37, 99, 235, 0.2) !important;
141 | animation: none !important;
142 | }
143 |
144 | .focus-reminder .button-hint {
145 | font-size: 13px !important;
146 | color: #94a3b8 !important;
147 | margin: 0 !important;
148 | }
149 |
150 | .focus-reminder button {
151 | all: revert !important;
152 | font-family: "Inter", -apple-system, sans-serif !important;
153 | border: none !important;
154 | cursor: pointer !important;
155 | transition: all 0.2s ease !important;
156 | margin: 0 !important;
157 | padding: 0 !important;
158 | background: none !important;
159 | line-height: normal !important;
160 | text-align: center !important;
161 | width: 100% !important;
162 | }
163 |
--------------------------------------------------------------------------------
/js/background.js:
--------------------------------------------------------------------------------
1 | chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
2 | if (message.action === 'closeTab' && sender.tab) {
3 | chrome.tabs.remove(sender.tab.id);
4 | }
5 | });
6 |
--------------------------------------------------------------------------------
/js/content.js:
--------------------------------------------------------------------------------
1 | const DELAY_TIME = 30
2 |
3 | async function shouldShowReminder() {
4 | try {
5 | const domain = window.location.hostname.replace('www.', '');
6 | const result = await chrome.storage.sync.get('sites');
7 |
8 | if (!result.sites || result.sites.length === 0) {
9 | return true;
10 | }
11 |
12 | const sites = result.sites;
13 | const matchingSite = sites.find(site =>
14 | domain.includes(site.domain) || site.domain.includes(domain)
15 | );
16 |
17 | return matchingSite ? matchingSite.enabled : false;
18 | } catch (error) {
19 | return false;
20 | }
21 | }
22 |
23 | async function createReminderDialog() {
24 | if (!document.body || document.documentElement.tagName.toLowerCase() === 'svg') {
25 | return;
26 | }
27 |
28 | if (document.querySelector('.focus-reminder')) {
29 | return;
30 | }
31 |
32 | const shouldShow = await shouldShowReminder();
33 |
34 | if (!shouldShow) {
35 | return;
36 | }
37 |
38 | const dialog = document.createElement('div');
39 | dialog.className = 'focus-reminder';
40 | dialog.innerHTML = `
41 |
Are you spending your time intentionally?
44 | 45 |If necessary...
47 |Look outside the window
and come back in a minute
Current website:
12 | 13 | 14 |