├── README.md
├── app.css
└── example_markup.html
/README.md:
--------------------------------------------------------------------------------
1 | # Bootstrap Tailwind components
2 | ## Tailwind components for common Bootstrap classes
3 |
4 | ## But why?
5 |
6 | I really like Tailwind but Bootstrap has been my goto CSS framework for last decades or so. I have also read and talked to people thinking that switching to Tailwind has ha steep(ish) learning curve and the benefits might not be visible in the first hours.
7 |
8 | So I made this so you can still get going with some common bootstrap components and have something visible immeditaly.
9 |
10 | However, this is by far not complete and that is not the point. It should just help you to get a smoother transition.
11 |
12 | ## How to install
13 |
14 | Basically you just need to follow the [Tailwind install guide](https://tailwindcss.com/docs/installation) and copy the classes from this repo to your own project.
15 |
16 | ## JS
17 |
18 | You can either pick the normal Bootstrap JS or use the smaller jQuery free [Bootstrap Native library](https://thednp.github.io/bootstrap.native/)
19 |
20 | ## Examples
21 |
22 | ### Alert
23 |
24 | ```html
25 |
26 | A simple primary alert‚ check it out!
27 |
28 | ```
29 |
30 | ```css
31 | .alert {
32 | @apply relative px-5 py-3 mb-4 rounded border border-transparent;
33 | }
34 |
35 | .alert-primary {
36 | @apply text-blue-800 bg-blue-200 border-blue-300;
37 | }
38 | ```
39 |
40 | ### Button
41 |
42 | ```html
43 | Primary
44 | ```
45 |
46 | ```css
47 | .btn {
48 | @apply inline-block font-normal text-center px-3 py-2 leading-normal text-base rounded cursor-pointer;
49 | }
50 |
51 | .btn-primary {
52 | @apply text-white bg-blue-600;
53 | }
54 |
55 | .btn-primary:hover {
56 | @apply text-white bg-blue-700
57 | }
58 | ```
59 |
60 | ### Card
61 |
62 | ```html
63 |
64 |
65 |
66 |
Card title
67 |
Some quick example text to build on the card title and make up the bulk of the card's content.
68 |
Go somewhere
69 |
70 |
71 | ```
72 |
73 | ```css
74 | .card {
75 | @apply flex flex-col relative bg-white rounded border border-gray-300;
76 | }
77 |
78 | .card-body {
79 | @apply flex-auto p-5;
80 | }
81 |
82 | .card-img-top {
83 | @apply w-full rounded-t;
84 | }
85 |
86 | .card-link + .card-link {
87 | @apply ml-5;
88 | }
89 |
90 | .card-text {
91 | @apply my-0 mb-4;
92 | }
93 |
94 | .card-title {
95 | @apply mb-3 text-xl;
96 | }
97 | ```
98 |
99 | ### Container
100 |
101 | The container class in Bootstrap can't be directly ported since Tailwind also have one so do it like:
102 |
103 | ```html
104 |
105 | <%= render @view_module, @view_template, assigns %>
106 |
107 | ```
108 |
109 | ### Navbar
110 |
111 | One thing to notice here is that the reponsive classes in Tailwind can't be extracted to components (AFAIK) and must still be in the markup.
112 |
113 | ```html
114 |
115 | Fixed navbar
116 |
117 |
118 |
119 |
132 |
133 | ```
134 |
135 | The CSS for this is a little much but it covers both light and dark mode in both collapsed and non collapsed mode.
136 |
137 | ```css
138 | .navbar {
139 | @apply relative flex flex-wrap items-center justify-between py-1 px-4;
140 | }
141 |
142 | .fixed-top {
143 | @apply fixed top-0 right-0 left-0 z-40
144 | }
145 |
146 | .navbar-brand {
147 | @apply inline-block py-1 mr-4 text-xl
148 | }
149 |
150 | .navbar-light .navbar-brand {
151 | @apply text-gray-900
152 | }
153 |
154 | .navbar-dark .navbar-brand {
155 | @apply text-gray-100
156 | }
157 |
158 | .navbar-toggler {
159 | @apply inline-block py-1 px-3 leading-none bg-transparent border border-transparent rounded
160 | }
161 |
162 | .navbar-light .navbar-toggler {
163 | @apply text-gray-500 border-gray-400
164 | }
165 |
166 | .navbar-dark .navbar-toggler {
167 | @apply text-gray-500 border-gray-700
168 | }
169 |
170 | .navbar-light .navbar-toggler-icon {
171 | background-image: url("data:image/svg+xml, ");
172 | }
173 |
174 | .navbar-dark .navbar-toggler-icon {
175 | background-image: url("data:image/svg+xml, ");
176 | }
177 |
178 | .navbar-toggler-icon {
179 | content: "";
180 | @apply inline-block w-6 h-6 align-middle bg-center bg-no-repeat
181 | }
182 |
183 | .navbar-collapse {
184 | @apply hidden w-full flex-grow items-center
185 | }
186 |
187 | .navbar-collapse.show, .navbar-collapse.in {
188 | @apply block
189 | }
190 |
191 | .navbar-nav {
192 | @apply flex flex-col pl-0 mb-0 list-none;
193 | }
194 |
195 | .nav-link {
196 | @apply block py-2 px-4
197 | }
198 |
199 | .collapse.show .nav-link, .collapse.in .nav-link {
200 | @apply px-0
201 | }
202 |
203 | .navbar-light .navbar-nav .nav-link {
204 | @apply text-gray-600
205 | }
206 |
207 | .navbar-light .navbar-nav .nav-link:hover {
208 | @apply text-gray-800
209 | }
210 |
211 | .navbar-light .navbar-nav .active > .nav-link, .navbar-light .navbar-nav .nav-link.active {
212 | @apply text-gray-900
213 | }
214 |
215 | .navbar-light .navbar-nav .nav-link.disabled {
216 | @apply text-gray-500 pointer-events-none
217 | }
218 |
219 | .navbar-dark .navbar-nav .nav-link {
220 | @apply text-gray-600
221 | }
222 |
223 | .navbar-dark .navbar-nav .nav-link:hover {
224 | @apply text-gray-500
225 | }
226 |
227 | .navbar-dark .navbar-nav .active > .nav-link, .navbar-dark .navbar-nav .nav-link.active {
228 | @apply text-gray-200
229 | }
230 |
231 | .navbar-dark .navbar-nav .nav-link.disabled {
232 | @apply text-gray-700 pointer-events-none
233 | }
234 | ```
235 |
236 | ### Form
237 |
238 | ```html
239 |
255 | ```
256 |
257 | ```css
258 | form {
259 | @apply block;
260 | }
261 |
262 | label {
263 | @apply inline-block mb-2;
264 | }
265 |
266 | .form-check {
267 | @apply relative block pl-5;
268 | }
269 |
270 | .form-group {
271 | @apply mb-4;
272 | }
273 |
274 | .form-check-input {
275 | @apply absolute mt-1 -ml-5;
276 | }
277 |
278 | .form-check-label {
279 | @apply mb-0;
280 | }
281 |
282 | .form-control {
283 | @apply block w-full py-2 px-3 text-base font-normal leading-normal text-gray-700 bg-white border border-gray-400 rounded;
284 | }
285 |
286 | .form-inline {
287 | @apply flex flex-wrap flex-row items-center
288 | }
289 |
290 | .form-inline .form-control {
291 | @apply inline-block w-auto align-middle
292 | }
293 |
294 | .form-text {
295 | @apply block mt-1;
296 | }
297 | ```
298 |
299 | ### Table
300 |
301 | ```html
302 |
303 |
304 |
305 | #
306 | First
307 | Last
308 | Handle
309 |
310 |
311 |
312 |
313 | 1
314 | Mark
315 | Otto
316 | @mdo
317 |
318 |
319 | 2
320 | Jacob
321 | Thornton
322 | @fat
323 |
324 |
325 | 3
326 | Larry the Bird
327 | @twitter
328 |
329 |
330 |
331 | ```
332 |
333 | ```css
334 | th {
335 | text-align: inherit;
336 | }
337 |
338 | .table {
339 | @apply w-full mb-4 text-gray-900
340 | }
341 |
342 | .table thead th {
343 | @apply align-bottom border-b-2 border-gray-300
344 | }
345 |
346 | .table td, .table th {
347 | @apply p-3 border-t border-gray-300
348 | }
349 |
350 | .table.table-bordered {
351 | @apply border border-gray-300
352 | }
353 |
354 | .table-bordered td, .table-bordered th {
355 | @apply border border-gray-300
356 | }
357 |
358 | ```
359 |
360 | ## Screenshots
361 |
362 | 
363 |
364 | 
365 |
366 | 
367 |
368 | 
369 |
370 | 
371 |
372 | 
373 |
374 |
--------------------------------------------------------------------------------
/app.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 |
4 | .h1, h1 {
5 | @apply text-4xl
6 | }
7 |
8 | .h2, h2 {
9 | @apply text-3xl
10 | }
11 |
12 | .h3, h3 {
13 | @apply text-2xl
14 | }
15 |
16 | .h4, h4 {
17 | @apply text-xl
18 | }
19 |
20 | .h5, h5 {
21 | @apply text-lg
22 | }
23 |
24 | .h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
25 | @apply mb-2 font-medium leading-tight
26 | }
27 |
28 | a {
29 | @apply text-blue-500;
30 | }
31 |
32 | a:hover {
33 | @apply text-blue-800;
34 | }
35 |
36 | .text-muted {
37 | @apply text-gray-600;
38 | }
39 |
40 | .bg-dark {
41 | @apply bg-gray-900;
42 | }
43 |
44 | .bg-light {
45 | @apply bg-gray-200;
46 | }
47 |
48 | .close {
49 | @apply float-right text-2xl font-bold leading-none text-black opacity-50
50 | }
51 |
52 | /*
53 | * ============================
54 | * Alerts
55 | *=============================
56 | */
57 |
58 | .alert {
59 | @apply relative px-5 py-3 mb-4 rounded border border-transparent;
60 | }
61 |
62 | .alert-primary {
63 | @apply text-blue-800 bg-blue-200 border-blue-300;
64 | }
65 |
66 | .alert-secondary {
67 | @apply text-gray-800 bg-gray-300 border-gray-400;
68 | }
69 |
70 | .alert-success {
71 | @apply text-green-800 bg-green-200 border-green-300;
72 | }
73 |
74 | .alert-danger {
75 | @apply text-red-800 bg-red-200 border-red-300;
76 | }
77 |
78 | .alert-warning {
79 | @apply text-yellow-800 bg-yellow-200 border-yellow-300;
80 | }
81 |
82 | .alert-info {
83 | @apply text-indigo-800 bg-indigo-200 border-indigo-300;
84 | }
85 |
86 | /*
87 | * ============================
88 | * Buttons
89 | *=============================
90 | */
91 |
92 | .btn {
93 | @apply inline-block font-normal text-center px-3 py-2 leading-normal text-base rounded cursor-pointer;
94 | }
95 |
96 | .btn-primary {
97 | @apply text-white bg-blue-600;
98 | }
99 |
100 | .btn-primary:hover {
101 | @apply text-white bg-blue-700
102 | }
103 |
104 | .btn-secondary {
105 | @apply text-white bg-gray-600;
106 | }
107 |
108 | .btn-secondary:hover {
109 | @apply text-white bg-gray-700
110 | }
111 |
112 | .btn-success {
113 | @apply text-white bg-green-600;
114 | }
115 |
116 | .btn-success:hover {
117 | @apply text-white bg-green-700
118 | }
119 |
120 | .btn-danger {
121 | @apply text-white bg-red-600;
122 | }
123 |
124 | .btn-danger:hover {
125 | @apply text-white bg-red-700
126 | }
127 |
128 | .btn-warning {
129 | @apply text-black bg-yellow-500;
130 | }
131 |
132 | .btn-warning:hover {
133 | @apply text-black bg-yellow-600
134 | }
135 |
136 | .btn-info {
137 | @apply text-white bg-teal-500;
138 | }
139 |
140 | .btn-info:hover {
141 | @apply text-white bg-teal-600
142 | }
143 |
144 | .btn-light {
145 | @apply text-black bg-gray-200;
146 | }
147 |
148 | .btn-light:hover {
149 | @apply text-black bg-gray-300
150 | }
151 |
152 | .btn-dark {
153 | @apply text-white bg-gray-900;
154 | }
155 |
156 | .btn-dark:hover {
157 | @apply text-white bg-black
158 | }
159 |
160 | .btn-link {
161 | @apply text-blue-500;
162 | }
163 |
164 | .btn-link:hover {
165 | @apply text-blue-800;
166 | }
167 |
168 |
169 | /*
170 | * ============================
171 | * Cards
172 | *=============================
173 | */
174 |
175 | .card {
176 | @apply flex flex-col relative bg-white rounded border border-gray-300;
177 | }
178 |
179 | .card-body {
180 | @apply flex-auto p-5;
181 | }
182 |
183 | .card-img-top {
184 | @apply w-full rounded-t;
185 | }
186 |
187 | .card-link + .card-link {
188 | @apply ml-5;
189 | }
190 |
191 | .card-text {
192 | @apply my-0 mb-4;
193 | }
194 |
195 | .card-title {
196 | @apply mb-3 text-xl;
197 | }
198 |
199 | /*
200 | * ============================
201 | * Dropdowns
202 | *=============================
203 | */
204 |
205 | .dropdown {
206 | @apply relative;
207 | }
208 |
209 | .dropdown-divider {
210 | @apply h-0 my-2 border-t border-gray-300;
211 | }
212 |
213 | .dropdown-menu {
214 | min-width: 10rem;
215 | @apply hidden z-20 float-left py-2 mt-1 text-base text-gray-900 list-none text-left border border-gray-300 bg-white rounded shadow;
216 | }
217 |
218 | .dropdown-menu.show, .dropdown.open .dropdown-menu {
219 | @apply block absolute top-0 left-0 mt-12;
220 | }
221 |
222 | .dropdown-item {
223 | @apply block w-full py-1 px-6 font-normal text-gray-900 bg-transparent border-0;
224 | }
225 | .dropdown-item:hover {
226 | @apply text-gray-900 bg-gray-100;
227 | }
228 |
229 | .dropdown-toggle::after {
230 | display: inline-block;
231 | margin-left: .255em;
232 | vertical-align: .255em;
233 | content: "";
234 | border-top: .3em solid;
235 | border-right: .3em solid transparent;
236 | border-bottom: 0;
237 | border-left: .3em solid transparent;
238 | }
239 |
240 | /*
241 | * ============================
242 | * Forms
243 | *=============================
244 | */
245 |
246 | form {
247 | @apply block;
248 | }
249 |
250 | label {
251 | @apply inline-block mb-2;
252 | }
253 |
254 | .form-check {
255 | @apply relative block pl-5;
256 | }
257 |
258 | .form-group {
259 | @apply mb-4;
260 | }
261 |
262 | .form-check-input {
263 | @apply absolute mt-1 -ml-5;
264 | }
265 |
266 | .form-check-label {
267 | @apply mb-0;
268 | }
269 |
270 | .form-control {
271 | @apply block w-full py-2 px-3 text-base font-normal leading-normal text-gray-700 bg-white border border-gray-400 rounded;
272 | }
273 |
274 | .form-inline {
275 | @apply flex flex-wrap flex-row items-center
276 | }
277 |
278 | .form-inline .form-control {
279 | @apply inline-block w-auto align-middle
280 | }
281 |
282 | .form-text {
283 | @apply block mt-1;
284 | }
285 |
286 |
287 | /*
288 | * ============================
289 | * List Groups
290 | *=============================
291 | */
292 |
293 | .list-group {
294 | @apply flex flex-col p-0 mb-0;
295 | }
296 |
297 | .list-group-item {
298 | margin-bottom: -1px;
299 | @apply relative block py-3 px-5 bg-white border border-gray-300;
300 | }
301 |
302 | .list-group-item:first-child {
303 | @apply rounded-t;
304 | }
305 |
306 | .list-group-item:last-child {
307 | @apply mb-0 rounded-b;
308 | }
309 |
310 | .list-group-item.disabled, .list-group-item:disabled {
311 | @apply text-gray-600 bg-white pointer-events-none
312 | }
313 |
314 | .list-group-item-action {
315 | @apply w-full text-gray-900;
316 | }
317 |
318 | .list-group-item-action:hover {
319 | @apply text-gray-900 bg-gray-100;
320 | }
321 |
322 | .list-group-item.active {
323 | @apply text-white bg-blue-600 z-10;
324 | }
325 |
326 |
327 | /*
328 | * ============================
329 | * Modals
330 | *=============================
331 | */
332 |
333 | .fade {
334 | transition: opacity .15s linear;
335 | }
336 |
337 | .modal {
338 | z-index: 1072;
339 | @apply hidden fixed top-0 left-0 w-full h-full outline-none
340 | }
341 |
342 | .modal.show, .modal.in {
343 | @apply block
344 | }
345 |
346 | .modal-dialog {
347 | @apply relative w-auto pointer-events-none max-w-lg my-8 mx-auto
348 | }
349 |
350 | .modal-content {
351 | @apply relative flex flex-col w-full pointer-events-auto bg-white border border-gray-300 rounded-lg
352 | }
353 |
354 | .modal-header {
355 | @apply flex items-start justify-between p-4 border-b border-gray-300 rounded-t
356 | }
357 |
358 | .modal-title {
359 | @apply mb-0 leading-normal
360 | }
361 |
362 | .modal-body {
363 | @apply relative flex p-4
364 | }
365 |
366 | .modal-footer {
367 | @apply flex items-center justify-end p-4 border-t border-gray-300
368 | }
369 |
370 | .modal-footer > :not(:last-child) {
371 | @apply mr-2
372 | }
373 |
374 | /*
375 | * ============================
376 | * Navbars
377 | *=============================
378 | */
379 |
380 | .navbar {
381 | @apply relative flex flex-wrap items-center justify-between py-1 px-4;
382 | }
383 |
384 | .fixed-top {
385 | @apply fixed top-0 right-0 left-0 z-40
386 | }
387 |
388 | .navbar-brand {
389 | @apply inline-block py-1 mr-4 text-xl
390 | }
391 |
392 | .navbar-light .navbar-brand {
393 | @apply text-gray-900
394 | }
395 |
396 | .navbar-dark .navbar-brand {
397 | @apply text-gray-100
398 | }
399 |
400 | .navbar-toggler {
401 | @apply inline-block py-1 px-3 leading-none bg-transparent border border-transparent rounded
402 | }
403 |
404 | .navbar-light .navbar-toggler {
405 | @apply text-gray-500 border-gray-400
406 | }
407 |
408 | .navbar-dark .navbar-toggler {
409 | @apply text-gray-500 border-gray-700
410 | }
411 |
412 | .navbar-light .navbar-toggler-icon {
413 | background-image: url("data:image/svg+xml, ");
414 | }
415 |
416 | .navbar-dark .navbar-toggler-icon {
417 | background-image: url("data:image/svg+xml, ");
418 | }
419 |
420 | .navbar-toggler-icon {
421 | content: "";
422 | @apply inline-block w-6 h-6 align-middle bg-center bg-no-repeat
423 | }
424 |
425 | .navbar-collapse {
426 | @apply hidden w-full flex-grow items-center
427 | }
428 |
429 | .navbar-collapse.show, .navbar-collapse.in {
430 | @apply block
431 | }
432 |
433 | .navbar-nav {
434 | @apply flex flex-col pl-0 mb-0 list-none;
435 | }
436 |
437 | .nav-link {
438 | @apply block py-2 px-4
439 | }
440 |
441 | .collapse.show .nav-link, .collapse.in .nav-link {
442 | @apply px-0
443 | }
444 |
445 | .navbar-light .navbar-nav .nav-link {
446 | @apply text-gray-600
447 | }
448 |
449 | .navbar-light .navbar-nav .nav-link:hover {
450 | @apply text-gray-800
451 | }
452 |
453 | .navbar-light .navbar-nav .active > .nav-link, .navbar-light .navbar-nav .nav-link.active {
454 | @apply text-gray-900
455 | }
456 |
457 | .navbar-light .navbar-nav .nav-link.disabled {
458 | @apply text-gray-500 pointer-events-none
459 | }
460 |
461 | .navbar-dark .navbar-nav .nav-link {
462 | @apply text-gray-600
463 | }
464 |
465 | .navbar-dark .navbar-nav .nav-link:hover {
466 | @apply text-gray-500
467 | }
468 |
469 | .navbar-dark .navbar-nav .active > .nav-link, .navbar-dark .navbar-nav .nav-link.active {
470 | @apply text-gray-200
471 | }
472 |
473 | .navbar-dark .navbar-nav .nav-link.disabled {
474 | @apply text-gray-700 pointer-events-none
475 | }
476 |
477 | /*
478 | * ============================
479 | * Tables
480 | *=============================
481 | */
482 |
483 | th {
484 | text-align: inherit;
485 | }
486 |
487 | .table {
488 | @apply w-full mb-4 text-gray-900
489 | }
490 |
491 | .table thead th {
492 | @apply align-bottom border-b-2 border-gray-300
493 | }
494 |
495 | .table td, .table th {
496 | @apply p-3 border-t border-gray-300
497 | }
498 |
499 | .table.table-bordered {
500 | @apply border border-gray-300
501 | }
502 |
503 | .table-bordered td, .table-bordered th {
504 | @apply border border-gray-300
505 | }
506 |
507 |
508 | @tailwind utilities;
509 |
--------------------------------------------------------------------------------
/example_markup.html:
--------------------------------------------------------------------------------
1 | Alerts
2 |
3 |
4 | A simple primary alert—check it out!
5 |
6 |
7 | A simple secondary alert—check it out!
8 |
9 |
10 | A simple success alert—check it out!
11 |
12 |
13 | A simple danger alert—check it out!
14 |
15 |
16 | A simple warning alert—check it out!
17 |
18 |
19 | A simple info alert—check it out!
20 |
21 |
22 | Buttons
23 |
24 | Primary
25 | Secondary
26 | Success
27 | Danger
28 | Warning
29 | Info
30 | Light
31 | Dark
32 | Link
33 |
34 |
35 |
36 | Cards
37 |
38 |
39 |
40 |
41 |
Card title
42 |
Some quick example text to build on the card title and make up the bulk of the card's content.
43 |
Go somewhere
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | This is some text within a card body.
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
Card title
60 |
Card subtitle
61 |
Some quick example text to build on the card title and make up the bulk of the card's content.
62 |
Card link
63 |
Another link
64 |
65 |
66 |
67 |
68 |
69 | Dropdowns
70 |
71 |
72 |
75 |
81 |
82 |
83 |
84 |
85 | Forms
86 |
87 |
103 |
104 |
105 |
106 |
107 | List Groups
108 |
109 |
110 | Cras justo odio
111 | Dapibus ac facilisis in
112 | Porta ac consectetur ac
113 | Vestibulum at eros
114 |
115 |
116 |
117 |
118 |
119 |
127 |
128 |
129 |
130 |
131 | Modals
132 |
133 |
134 |
135 |
136 |
142 |
143 |
Modal body text goes here.
144 |
145 |
149 |
150 |
151 |
152 |
153 |
154 | Launch simple modal
155 |
156 |
157 |
158 |
159 |
160 | Navbars
161 |
162 |
163 | Navbar
164 |
165 |
166 |
167 |
175 |
176 |
177 |
178 |
179 |
180 | Navbar
181 |
182 |
183 |
184 |
192 |
193 |
194 |
195 |
196 | Navbar
197 |
201 |
202 |
203 |
204 |
205 |
206 | Navbar
207 |
211 |
212 |
213 |
214 |
215 |
216 | Tables
217 |
218 |
219 |
220 |
221 | #
222 | First
223 | Last
224 | Handle
225 |
226 |
227 |
228 |
229 | 1
230 | Mark
231 | Otto
232 | @mdo
233 |
234 |
235 | 2
236 | Jacob
237 | Thornton
238 | @fat
239 |
240 |
241 | 3
242 | Larry
243 | the Bird
244 | @twitter
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 | #
256 | First
257 | Last
258 | Handle
259 |
260 |
261 |
262 |
263 | 1
264 | Mark
265 | Otto
266 | @mdo
267 |
268 |
269 | 2
270 | Jacob
271 | Thornton
272 | @fat
273 |
274 |
275 | 3
276 | Larry the Bird
277 | @twitter
278 |
279 |
280 |
281 |
--------------------------------------------------------------------------------