plum-cms 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +20 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +252 -0
  5. data/app/assets/builds/tailwind.css +2 -0
  6. data/app/assets/images/plum-mark.svg +7 -0
  7. data/app/assets/images/table-needs-logo.svg +83 -0
  8. data/app/assets/javascripts/plum/application.js +15 -0
  9. data/app/assets/stylesheets/application.css +10 -0
  10. data/app/assets/stylesheets/plum/control_panel.css +2 -0
  11. data/app/assets/tailwind/application.css +47 -0
  12. data/app/controllers/plum/application_controller.rb +34 -0
  13. data/app/controllers/plum/cp/assets_controller.rb +84 -0
  14. data/app/controllers/plum/cp/base_controller.rb +45 -0
  15. data/app/controllers/plum/cp/content_types_controller.rb +59 -0
  16. data/app/controllers/plum/cp/dashboard_controller.rb +10 -0
  17. data/app/controllers/plum/cp/entries_controller.rb +240 -0
  18. data/app/controllers/plum/cp/form_definitions_controller.rb +105 -0
  19. data/app/controllers/plum/cp/form_submissions_controller.rb +26 -0
  20. data/app/controllers/plum/cp/globals_controller.rb +84 -0
  21. data/app/controllers/plum/cp/nav_items_controller.rb +68 -0
  22. data/app/controllers/plum/cp/nav_menus_controller.rb +56 -0
  23. data/app/controllers/plum/cp/site_settings_controller.rb +87 -0
  24. data/app/controllers/plum/cp/taxonomies_controller.rb +56 -0
  25. data/app/controllers/plum/cp/terms_controller.rb +55 -0
  26. data/app/controllers/plum/cp/theme_previews_controller.rb +27 -0
  27. data/app/controllers/plum/cp/themes_controller.rb +98 -0
  28. data/app/controllers/plum/form_submissions_controller.rb +44 -0
  29. data/app/controllers/plum/pages_controller.rb +161 -0
  30. data/app/controllers/plum/sessions_controller.rb +25 -0
  31. data/app/controllers/plum/theme_assets_controller.rb +14 -0
  32. data/app/helpers/application_helper.rb +2 -0
  33. data/app/helpers/markdown_helper.rb +21 -0
  34. data/app/helpers/plum/cp/assets_helper.rb +21 -0
  35. data/app/helpers/plum/cp/theme_settings_helper.rb +26 -0
  36. data/app/javascript/application.js +18 -0
  37. data/app/javascript/controllers/application.js +9 -0
  38. data/app/javascript/controllers/index.js +4 -0
  39. data/app/javascript/controllers/plum/blocks_editor_controller.js +275 -0
  40. data/app/javascript/controllers/plum/blueprint_controller.js +120 -0
  41. data/app/javascript/controllers/plum/form_fields_controller.js +84 -0
  42. data/app/javascript/controllers/plum/image_picker_controller.js +143 -0
  43. data/app/javascript/controllers/plum/theme_settings_controller.js +17 -0
  44. data/app/models/plum/application_record.rb +5 -0
  45. data/app/models/plum/asset.rb +69 -0
  46. data/app/models/plum/content_type.rb +35 -0
  47. data/app/models/plum/entry.rb +67 -0
  48. data/app/models/plum/entry_term.rb +8 -0
  49. data/app/models/plum/form_definition.rb +68 -0
  50. data/app/models/plum/form_submission.rb +76 -0
  51. data/app/models/plum/global.rb +24 -0
  52. data/app/models/plum/nav_item.rb +58 -0
  53. data/app/models/plum/nav_menu.rb +24 -0
  54. data/app/models/plum/site.rb +112 -0
  55. data/app/models/plum/site_scoped.rb +17 -0
  56. data/app/models/plum/site_setting.rb +35 -0
  57. data/app/models/plum/taxonomy.rb +24 -0
  58. data/app/models/plum/term.rb +23 -0
  59. data/app/models/plum/user.rb +16 -0
  60. data/app/services/plum/base_blocks.rb +161 -0
  61. data/app/services/plum/block_editor_config.rb +65 -0
  62. data/app/services/plum/block_library.rb +31 -0
  63. data/app/services/plum/builder_renderer.rb +63 -0
  64. data/app/services/plum/field_expander.rb +78 -0
  65. data/app/services/plum/form_renderer.rb +110 -0
  66. data/app/services/plum/liquid_context.rb +335 -0
  67. data/app/services/plum/liquid_filters.rb +58 -0
  68. data/app/services/plum/liquid_renderer.rb +49 -0
  69. data/app/services/plum/theme.rb +160 -0
  70. data/app/services/plum/theme_asset_path.rb +31 -0
  71. data/app/services/plum/theme_package_installer.rb +287 -0
  72. data/app/services/plum/theme_registry.rb +63 -0
  73. data/app/services/plum/theme_resolver.rb +55 -0
  74. data/app/services/plum/theme_settings_params.rb +54 -0
  75. data/app/themes/bagel-boy/assets/screenshot.svg +92 -0
  76. data/app/themes/bagel-boy/assets/theme.css +184 -0
  77. data/app/themes/bagel-boy/blocks/hours.liquid +4 -0
  78. data/app/themes/bagel-boy/blocks/menu_item.liquid +7 -0
  79. data/app/themes/bagel-boy/layouts/base.liquid +64 -0
  80. data/app/themes/bagel-boy/templates/collections/posts.liquid +32 -0
  81. data/app/themes/bagel-boy/templates/entries/_default.liquid +13 -0
  82. data/app/themes/bagel-boy/templates/entries/landing.liquid +4 -0
  83. data/app/themes/bagel-boy/templates/entries/pages.liquid +18 -0
  84. data/app/themes/bagel-boy/templates/entries/posts.liquid +8 -0
  85. data/app/themes/bagel-boy/templates/index.liquid +10 -0
  86. data/app/themes/bagel-boy/templates/search.liquid +28 -0
  87. data/app/themes/bagel-boy/theme.yml +47 -0
  88. data/app/themes/bagel-shop/assets/screenshot.svg +28 -0
  89. data/app/themes/bagel-shop/assets/theme.css +7 -0
  90. data/app/themes/bagel-shop/layouts/base.liquid +507 -0
  91. data/app/themes/bagel-shop/templates/entries/_default.liquid +15 -0
  92. data/app/themes/bagel-shop/templates/entries/pages.liquid +12 -0
  93. data/app/themes/bagel-shop/templates/entries/posts.liquid +19 -0
  94. data/app/themes/bagel-shop/templates/index.liquid +55 -0
  95. data/app/themes/bagel-shop/theme.yml +30 -0
  96. data/app/themes/default/assets/screenshot.svg +24 -0
  97. data/app/themes/default/assets/theme.css +89 -0
  98. data/app/themes/default/layouts/base.liquid +117 -0
  99. data/app/themes/default/templates/collections/_default.liquid +16 -0
  100. data/app/themes/default/templates/collections/posts.liquid +27 -0
  101. data/app/themes/default/templates/entries/_default.liquid +26 -0
  102. data/app/themes/default/templates/entries/landing.liquid +5 -0
  103. data/app/themes/default/templates/entries/pages.liquid +17 -0
  104. data/app/themes/default/templates/entries/posts.liquid +20 -0
  105. data/app/themes/default/templates/index.liquid +22 -0
  106. data/app/themes/default/templates/search.liquid +28 -0
  107. data/app/themes/default/templates/taxonomies/_default.liquid +25 -0
  108. data/app/themes/default/templates/taxonomies/_default_index.liquid +14 -0
  109. data/app/themes/default/theme.yml +23 -0
  110. data/app/views/layouts/plum/cp.html.erb +138 -0
  111. data/app/views/layouts/plum/session.html.erb +43 -0
  112. data/app/views/plum/cp/assets/_form.html.erb +44 -0
  113. data/app/views/plum/cp/assets/edit.html.erb +14 -0
  114. data/app/views/plum/cp/assets/index.html.erb +52 -0
  115. data/app/views/plum/cp/assets/new.html.erb +9 -0
  116. data/app/views/plum/cp/content_types/_form.html.erb +113 -0
  117. data/app/views/plum/cp/content_types/edit.html.erb +8 -0
  118. data/app/views/plum/cp/content_types/index.html.erb +48 -0
  119. data/app/views/plum/cp/content_types/new.html.erb +5 -0
  120. data/app/views/plum/cp/content_types/show.html.erb +59 -0
  121. data/app/views/plum/cp/dashboard/show.html.erb +50 -0
  122. data/app/views/plum/cp/entries/_form.html.erb +196 -0
  123. data/app/views/plum/cp/entries/edit.html.erb +9 -0
  124. data/app/views/plum/cp/entries/index.html.erb +51 -0
  125. data/app/views/plum/cp/entries/new.html.erb +5 -0
  126. data/app/views/plum/cp/form_definitions/_form.html.erb +90 -0
  127. data/app/views/plum/cp/form_definitions/edit.html.erb +11 -0
  128. data/app/views/plum/cp/form_definitions/index.html.erb +43 -0
  129. data/app/views/plum/cp/form_definitions/new.html.erb +5 -0
  130. data/app/views/plum/cp/form_definitions/show.html.erb +70 -0
  131. data/app/views/plum/cp/form_submissions/show.html.erb +20 -0
  132. data/app/views/plum/cp/globals/_form.html.erb +44 -0
  133. data/app/views/plum/cp/globals/edit.html.erb +11 -0
  134. data/app/views/plum/cp/globals/index.html.erb +41 -0
  135. data/app/views/plum/cp/globals/new.html.erb +5 -0
  136. data/app/views/plum/cp/globals/show.html.erb +14 -0
  137. data/app/views/plum/cp/nav_items/_form.html.erb +56 -0
  138. data/app/views/plum/cp/nav_items/edit.html.erb +11 -0
  139. data/app/views/plum/cp/nav_items/new.html.erb +6 -0
  140. data/app/views/plum/cp/nav_menus/_form.html.erb +33 -0
  141. data/app/views/plum/cp/nav_menus/_nav_item.html.erb +24 -0
  142. data/app/views/plum/cp/nav_menus/edit.html.erb +11 -0
  143. data/app/views/plum/cp/nav_menus/index.html.erb +41 -0
  144. data/app/views/plum/cp/nav_menus/new.html.erb +5 -0
  145. data/app/views/plum/cp/nav_menus/show.html.erb +24 -0
  146. data/app/views/plum/cp/shared/_icon.html.erb +39 -0
  147. data/app/views/plum/cp/shared/_image_picker.html.erb +38 -0
  148. data/app/views/plum/cp/site_settings/edit.html.erb +145 -0
  149. data/app/views/plum/cp/site_settings/show.html.erb +62 -0
  150. data/app/views/plum/cp/taxonomies/_form.html.erb +38 -0
  151. data/app/views/plum/cp/taxonomies/edit.html.erb +2 -0
  152. data/app/views/plum/cp/taxonomies/index.html.erb +38 -0
  153. data/app/views/plum/cp/taxonomies/new.html.erb +2 -0
  154. data/app/views/plum/cp/taxonomies/show.html.erb +46 -0
  155. data/app/views/plum/cp/terms/_form.html.erb +30 -0
  156. data/app/views/plum/cp/terms/edit.html.erb +2 -0
  157. data/app/views/plum/cp/terms/new.html.erb +2 -0
  158. data/app/views/plum/cp/themes/index.html.erb +160 -0
  159. data/app/views/plum/form_mailer/submission_notification.html.erb +13 -0
  160. data/app/views/plum/form_mailer/submission_notification.text.erb +7 -0
  161. data/app/views/plum/sessions/new.html.erb +20 -0
  162. data/compat/plum-cms.rb +1 -0
  163. data/config/locales/en.yml +31 -0
  164. data/config/plum_importmap.rb +4 -0
  165. data/config/plum_routes.rb +34 -0
  166. data/db/engine_migrate/20260101000001_create_plum_tables.rb +154 -0
  167. data/lib/generators/plum/install/install_generator.rb +54 -0
  168. data/lib/generators/plum/install/templates/plum_initializer.rb +37 -0
  169. data/lib/plum/configuration.rb +88 -0
  170. data/lib/plum/content_source.rb +45 -0
  171. data/lib/plum/content_source_context.rb +30 -0
  172. data/lib/plum/content_source_registry.rb +120 -0
  173. data/lib/plum/engine.rb +30 -0
  174. data/lib/plum/liquid_tags/form_tag.rb +17 -0
  175. data/lib/plum/version.rb +7 -0
  176. data/lib/plum.rb +11 -0
  177. data/vendor/javascript/lexxy.js +11859 -0
  178. metadata +405 -0
@@ -0,0 +1,92 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 750" role="img" aria-labelledby="title desc">
2
+ <title id="title">Bagel Boy theme preview</title>
3
+ <desc id="desc">A polished preview of the Bagel Boy theme with a retro bakery header, hero section, menu cards, and hours panel.</desc>
4
+
5
+ <defs>
6
+ <filter id="soft-shadow" x="-20%" y="-20%" width="140%" height="140%">
7
+ <feDropShadow dx="0" dy="16" stdDeviation="18" flood-color="#0C0B0A" flood-opacity=".15"/>
8
+ </filter>
9
+ <pattern id="tile" width="58" height="58" patternUnits="userSpaceOnUse">
10
+ <path d="M0 58L58 0" stroke="#FDC694" stroke-width="4" stroke-opacity=".28"/>
11
+ </pattern>
12
+ <radialGradient id="bagel" cx="34%" cy="28%" r="72%">
13
+ <stop offset="0" stop-color="#FFE4A2"/>
14
+ <stop offset=".48" stop-color="#C87734"/>
15
+ <stop offset="1" stop-color="#9B4D1F"/>
16
+ </radialGradient>
17
+ </defs>
18
+
19
+ <rect width="1200" height="750" fill="#FFF6E9"/>
20
+ <rect width="1200" height="750" fill="url(#tile)" opacity=".55"/>
21
+
22
+ <g filter="url(#soft-shadow)">
23
+ <rect x="70" y="56" width="1060" height="640" rx="24" fill="#FEFEFE"/>
24
+ </g>
25
+
26
+ <rect x="70" y="56" width="1060" height="640" rx="24" fill="#FEFEFE" stroke="#0C0B0A" stroke-opacity=".14" stroke-width="3"/>
27
+ <rect x="70" y="56" width="1060" height="112" rx="24" fill="#FEFEFE"/>
28
+ <path d="M70 144H1130" stroke="#FB404C" stroke-width="8"/>
29
+
30
+ <g transform="translate(116 82)">
31
+ <circle cx="40" cy="40" r="37" fill="#FB404C"/>
32
+ <circle cx="40" cy="40" r="15" fill="#FEFEFE"/>
33
+ <circle cx="25" cy="24" r="4.5" fill="#FCDE5A"/>
34
+ <circle cx="50" cy="19" r="4" fill="#FCDE5A"/>
35
+ <circle cx="57" cy="50" r="4.5" fill="#FCDE5A"/>
36
+ <text x="92" y="47" fill="#FB404C" font-family="Arial Black, Arial, sans-serif" font-size="36" font-weight="900">Bagel Boy</text>
37
+ </g>
38
+
39
+ <g fill="#0C0B0A" font-family="Arial, sans-serif" font-size="18" font-weight="700">
40
+ <text x="775" y="126">Menu</text>
41
+ <text x="858" y="126">Hours</text>
42
+ <text x="943" y="126">Order</text>
43
+ </g>
44
+ <rect x="1024" y="95" width="76" height="38" rx="19" fill="#FCDE5A"/>
45
+ <text x="1043" y="120" fill="#0C0B0A" font-family="Arial, sans-serif" font-size="15" font-weight="900">Visit</text>
46
+
47
+ <g transform="translate(116 214)">
48
+ <rect x="0" y="0" width="608" height="302" rx="26" fill="#FFF6E9"/>
49
+ <text x="34" y="68" fill="#FB404C" font-family="Arial Black, Arial, sans-serif" font-size="74" font-weight="900">Big bagel</text>
50
+ <text x="34" y="145" fill="#FB404C" font-family="Arial Black, Arial, sans-serif" font-size="74" font-weight="900">energy.</text>
51
+ <rect x="38" y="184" width="342" height="15" rx="7.5" fill="#0C0B0A" opacity=".82"/>
52
+ <rect x="38" y="214" width="432" height="15" rx="7.5" fill="#0C0B0A" opacity=".34"/>
53
+ <rect x="38" y="244" width="252" height="15" rx="7.5" fill="#0C0B0A" opacity=".34"/>
54
+ <rect x="408" y="232" width="142" height="46" rx="23" fill="#FB404C"/>
55
+ <text x="438" y="262" fill="#FEFEFE" font-family="Arial, sans-serif" font-size="18" font-weight="900">Order</text>
56
+ </g>
57
+
58
+ <g transform="translate(745 206)">
59
+ <rect x="28" y="40" width="270" height="284" rx="24" fill="#FDC694" transform="rotate(3 163 182)"/>
60
+ <rect x="50" y="28" width="270" height="284" rx="24" fill="#FCDE5A" transform="rotate(-5 185 170)"/>
61
+ <circle cx="184" cy="178" r="122" fill="url(#bagel)"/>
62
+ <circle cx="184" cy="178" r="43" fill="#FFF6E9"/>
63
+ <circle cx="139" cy="125" r="8" fill="#FCDE5A"/>
64
+ <circle cx="207" cy="105" r="7" fill="#FCDE5A"/>
65
+ <circle cx="247" cy="167" r="8" fill="#FCDE5A"/>
66
+ <circle cx="135" cy="221" r="7" fill="#FCDE5A"/>
67
+ <circle cx="205" cy="246" r="6" fill="#FCDE5A"/>
68
+ <path d="M58 292C116 318 246 318 306 284" fill="none" stroke="#0C0B0A" stroke-width="12" stroke-linecap="round" opacity=".85"/>
69
+ </g>
70
+
71
+ <g transform="translate(116 556)">
72
+ <rect width="248" height="94" rx="18" fill="#FEFEFE" stroke="#FB404C" stroke-width="4"/>
73
+ <text x="24" y="38" fill="#0C0B0A" font-family="Arial Black, Arial, sans-serif" font-size="22" font-weight="900">Poppy Plain</text>
74
+ <rect x="24" y="56" width="142" height="11" rx="5.5" fill="#0C0B0A" opacity=".32"/>
75
+ <text x="199" y="62" fill="#FB404C" font-family="Arial, sans-serif" font-size="21" font-weight="900">$4</text>
76
+ </g>
77
+
78
+ <g transform="translate(394 556)">
79
+ <rect width="248" height="94" rx="18" fill="#FEFEFE" stroke="#FDC694" stroke-width="4"/>
80
+ <text x="24" y="38" fill="#0C0B0A" font-family="Arial Black, Arial, sans-serif" font-size="22" font-weight="900">Chip Salt</text>
81
+ <rect x="24" y="56" width="156" height="11" rx="5.5" fill="#0C0B0A" opacity=".32"/>
82
+ <text x="199" y="62" fill="#FB404C" font-family="Arial, sans-serif" font-size="21" font-weight="900">$5</text>
83
+ </g>
84
+
85
+ <g transform="translate(672 556)">
86
+ <rect width="404" height="94" rx="18" fill="#0C0B0A"/>
87
+ <text x="28" y="38" fill="#FCDE5A" font-family="Arial Black, Arial, sans-serif" font-size="23" font-weight="900">Open early every day</text>
88
+ <rect x="28" y="57" width="224" height="12" rx="6" fill="#FFF6E9" opacity=".7"/>
89
+ <rect x="326" y="31" width="50" height="20" rx="10" fill="#FB404C"/>
90
+ <rect x="284" y="62" width="82" height="12" rx="6" fill="#FFF6E9" opacity=".35"/>
91
+ </g>
92
+ </svg>
@@ -0,0 +1,184 @@
1
+ /* Bagel Boy — retro neighborhood bakery
2
+ Official designer palette: Blood Burst #FB404C, Potato Chip #FCDE5A,
3
+ Blushing Cinnamon #FDC694, Soft Pillow #FFF6E9, Brilliance #FEFEFE, Raven #0C0B0A. */
4
+ :root {
5
+ --bb-bg: #FFF6E9; /* Soft Pillow */
6
+ --bb-surface: #FEFEFE; /* Brilliance */
7
+ --bb-yellow: #FCDE5A; /* Potato Chip */
8
+ --bb-ink: #0C0B0A; /* Raven */
9
+ --bb-muted: #6f655b;
10
+ /* --bb-poppy (Blood Burst) and --bb-amber (Blushing Cinnamon) injected per-site in base.liquid */
11
+ }
12
+
13
+ * { box-sizing: border-box; margin: 0; padding: 0; }
14
+
15
+ body {
16
+ background: var(--bb-bg);
17
+ color: var(--bb-ink);
18
+ font-family: "Nunito Sans", ui-sans-serif, system-ui, sans-serif;
19
+ line-height: 1.65;
20
+ display: flex;
21
+ flex-direction: column;
22
+ min-height: 100vh;
23
+ }
24
+
25
+ h1, h2, h3 {
26
+ font-family: "Lilita One", ui-rounded, "Segoe UI", sans-serif;
27
+ font-weight: 400;
28
+ letter-spacing: 0.01em;
29
+ color: var(--bb-poppy);
30
+ line-height: 1.05;
31
+ }
32
+
33
+ a { color: var(--bb-poppy); }
34
+
35
+ .bb-wrap { width: min(1040px, calc(100% - 2.5rem)); margin: 0 auto; }
36
+
37
+ /* Header */
38
+ .bb-header {
39
+ background: var(--bb-surface);
40
+ border-bottom: 4px solid var(--bb-poppy);
41
+ }
42
+ .bb-header-inner {
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: space-between;
46
+ gap: 1rem;
47
+ padding: 0.9rem 0;
48
+ }
49
+ .bb-brand { display: inline-flex; align-items: center; text-decoration: none; }
50
+ .bb-logo { max-height: 78px; width: auto; display: block; }
51
+ .bb-brand-text {
52
+ font-family: "Lilita One", ui-rounded, sans-serif;
53
+ font-size: 1.9rem;
54
+ color: var(--bb-poppy);
55
+ }
56
+ .bb-nav { display: flex; flex-wrap: wrap; gap: 0.4rem 1.25rem; }
57
+ .bb-nav a {
58
+ color: var(--bb-ink);
59
+ font-weight: 700;
60
+ text-decoration: none;
61
+ font-size: 0.98rem;
62
+ }
63
+ .bb-nav a:hover { color: var(--bb-poppy); }
64
+
65
+ main { flex: 1; }
66
+
67
+ /* Generic block rhythm */
68
+ .plum-block { margin: 3rem 0; }
69
+ .bb-entry { padding: 2.5rem 0 4rem; }
70
+ .bb-entry-image { width: 100%; border-radius: 1rem; margin: 1.5rem 0; }
71
+ .prose p { margin-bottom: 1rem; }
72
+ .prose h2 { margin: 2rem 0 1rem; }
73
+ .prose ul, .prose ol { margin: 0 0 1rem 1.5rem; }
74
+ .prose img { max-width: 100%; height: auto; border-radius: 0.5rem; }
75
+
76
+ /* Collection / index pages */
77
+ .bb-collection { padding: 2.5rem 0 4rem; }
78
+ .bb-collection h1 { font-size: 2.5rem; color: var(--bb-poppy); margin-bottom: 2rem; }
79
+ .bb-post-card { margin-bottom: 2.5rem; padding-bottom: 2.5rem; border-bottom: 1px solid var(--bb-cinnamon); }
80
+ .bb-post-card:last-child { border-bottom: none; }
81
+ .bb-pagination { display: flex; gap: 1rem; justify-content: center; margin-top: 2rem; }
82
+ .bb-pagination a { color: var(--bb-poppy); text-decoration: none; font-weight: bold; }
83
+ .bb-pagination a:hover { text-decoration: underline; }
84
+ .bb-post-card h2 { margin: 0.5rem 0 0.25rem; }
85
+ .bb-post-card h2 a { color: var(--bb-ink); text-decoration: none; }
86
+ .bb-post-card h2 a:hover { color: var(--bb-poppy); }
87
+ .bb-post-card-image { width: 100%; max-height: 300px; object-fit: cover; border-radius: 1rem; }
88
+ .bb-post-meta { color: #888; font-size: 0.875rem; margin: 0.25rem 0 0.5rem; }
89
+
90
+ /* Base blocks */
91
+ .plum-block-hero { text-align: center; padding: 1.5rem 0; }
92
+ .plum-block-hero-image { width: 100%; max-height: 460px; object-fit: cover; border-radius: 1.25rem; margin-bottom: 1.5rem; }
93
+ .plum-block-hero h2 { font-size: 3.4rem; color: var(--bb-poppy); }
94
+ .plum-block-hero p { font-size: 1.25rem; color: var(--bb-ink); max-width: 42rem; margin: 0.6rem auto 0; }
95
+
96
+ .plum-block-image img { width: 100%; border-radius: 1rem; }
97
+ .plum-block-image figcaption { text-align: center; color: var(--bb-muted); font-size: 0.9rem; margin-top: 0.5rem; }
98
+
99
+ .plum-block-image-text { display: grid; grid-template-columns: 1fr 1fr; gap: 2.5rem; align-items: center; }
100
+ .plum-block-image-text.plum-image-right .plum-block-image-text-media { order: 2; }
101
+ .plum-block-image-text-media img { width: 100%; border-radius: 1rem; }
102
+
103
+ .plum-block-cta {
104
+ text-align: center;
105
+ background: var(--bb-amber);
106
+ border-radius: 1.25rem;
107
+ padding: 2.75rem 1.5rem;
108
+ }
109
+ .plum-block-cta h2 { color: var(--bb-ink); }
110
+ .plum-block-cta-button, .plum-block-button a {
111
+ display: inline-block;
112
+ margin-top: 1rem;
113
+ background: var(--bb-poppy);
114
+ color: #fff;
115
+ text-decoration: none;
116
+ font-weight: 700;
117
+ padding: 0.8rem 1.6rem;
118
+ border-radius: 999px;
119
+ }
120
+
121
+ .plum-block-gallery-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
122
+ .plum-block-gallery-grid img { width: 100%; aspect-ratio: 1/1; object-fit: cover; border-radius: 0.75rem; }
123
+
124
+ .plum-block-spacer.plum-spacer-small { height: 1rem; }
125
+ .plum-block-spacer.plum-spacer-medium { height: 2.5rem; }
126
+ .plum-block-spacer.plum-spacer-large { height: 5rem; }
127
+
128
+ /* Bagel Boy custom blocks */
129
+ .bb-menu-item {
130
+ display: flex;
131
+ flex-direction: column;
132
+ gap: 0.15rem;
133
+ padding: 0.9rem 0;
134
+ border-bottom: 2px dotted rgba(176, 84, 76, 0.4);
135
+ margin: 0;
136
+ }
137
+ .bb-menu-item-head { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; }
138
+ .bb-menu-item-name { font-weight: 700; color: var(--bb-ink); font-size: 1.1rem; }
139
+ .bb-menu-item-price { color: var(--bb-poppy); font-weight: 800; white-space: nowrap; }
140
+ .bb-menu-item-desc { color: var(--bb-muted); font-size: 0.95rem; }
141
+
142
+ .bb-hours { text-align: center; background: var(--bb-surface); border-radius: 1.25rem; padding: 2rem 1.5rem; }
143
+ .bb-hours h2 { color: var(--bb-poppy); }
144
+ .bb-hours-schedule {
145
+ font-family: "Nunito Sans", sans-serif;
146
+ font-size: 1.05rem;
147
+ line-height: 1.9;
148
+ white-space: pre-wrap;
149
+ color: var(--bb-ink);
150
+ }
151
+
152
+ /* Forms */
153
+ .plum-form { display: grid; gap: 1rem; max-width: 36rem; }
154
+ .plum-form-field { display: grid; gap: 0.35rem; }
155
+ .plum-form label { font-weight: 700; color: var(--bb-ink); font-size: 0.9rem; }
156
+ .plum-form input[type="text"], .plum-form input[type="email"], .plum-form textarea, .plum-form select {
157
+ width: 100%; border: 2px solid rgba(42, 38, 34, 0.25); border-radius: 0.6rem; padding: 0.65rem 0.8rem; font: inherit; background: #fff;
158
+ }
159
+ .plum-form textarea { min-height: 8rem; }
160
+ .plum-form-submit {
161
+ justify-self: start; border: 0; border-radius: 999px;
162
+ background: var(--bb-poppy); color: #fff; padding: 0.75rem 1.6rem; font-weight: 700; cursor: pointer;
163
+ }
164
+
165
+ /* Footer */
166
+ .bb-footer { background: var(--bb-ink); color: var(--bb-bg); margin-top: 3rem; }
167
+ .bb-footer-inner {
168
+ display: flex; flex-wrap: wrap; gap: 1rem 2rem; justify-content: space-between;
169
+ padding: 2rem 0; font-size: 0.95rem;
170
+ }
171
+ .bb-footer a { color: var(--bb-amber); }
172
+ .bb-footer-social { display: flex; gap: 1.25rem; }
173
+ .bb-powered { opacity: 0.7; }
174
+
175
+ .bb-hero-fallback { text-align: center; padding: 5rem 0; }
176
+ .bb-empty-note { color: var(--bb-muted); margin-top: 1rem; }
177
+
178
+ @media (max-width: 720px) {
179
+ .plum-block-image-text { grid-template-columns: 1fr; }
180
+ .plum-block-image-text.plum-image-right .plum-block-image-text-media { order: 0; }
181
+ .plum-block-gallery-grid { grid-template-columns: 1fr 1fr; }
182
+ .plum-block-hero h2 { font-size: 2.4rem; }
183
+ .bb-header-inner { flex-direction: column; align-items: flex-start; }
184
+ }
@@ -0,0 +1,4 @@
1
+ <section class="plum-block bb-hours">
2
+ {% if block.heading %}<h2>{{ block.heading }}</h2>{% endif %}
3
+ {% if block.schedule %}<pre class="bb-hours-schedule">{{ block.schedule }}</pre>{% endif %}
4
+ </section>
@@ -0,0 +1,7 @@
1
+ <div class="plum-block bb-menu-item">
2
+ <div class="bb-menu-item-head">
3
+ {% if block.name %}<span class="bb-menu-item-name">{{ block.name }}</span>{% endif %}
4
+ {% if block.price %}<span class="bb-menu-item-price">{{ block.price }}</span>{% endif %}
5
+ </div>
6
+ {% if block.description %}<p class="bb-menu-item-desc">{{ block.description }}</p>{% endif %}
7
+ </div>
@@ -0,0 +1,64 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{{ entry.title | default: site.seo_title | default: site.name }}</title>
7
+ <meta name="description" content="{{ site.seo_description | default: site.tagline }}">
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Lilita+One&family=Nunito+Sans:wght@400;600;700&display=swap" rel="stylesheet">
11
+ <link rel="stylesheet" href="{{ 'theme.css' | theme_asset_url }}">
12
+ <style>
13
+ :root {
14
+ --bb-poppy: {{ site.theme_settings.accent_color | default: '#FB404C' }};
15
+ --bb-amber: {{ site.theme_settings.secondary_color | default: '#FDC694' }};
16
+ }
17
+ </style>
18
+ {% if site.custom_css %}<style id="plum-custom-css">{{ site.custom_css }}</style>{% endif %}
19
+ </head>
20
+ <body>
21
+ <header class="bb-header">
22
+ <div class="bb-wrap bb-header-inner">
23
+ <a class="bb-brand" href="{{ site.url }}">
24
+ {% if site.logo.url %}
25
+ <img class="bb-logo" src="{{ site.logo.url }}" alt="{{ site.logo.alt_text | default: site.name }}">
26
+ {% else %}
27
+ <span class="bb-brand-text">{{ site.name }}</span>
28
+ {% endif %}
29
+ </a>
30
+ {% if nav.main.items.size > 0 %}
31
+ <nav class="bb-nav" aria-label="Main navigation">
32
+ {% for item in nav.main.items %}
33
+ <a href="{{ item.url }}">{{ item.label }}</a>
34
+ {% endfor %}
35
+ </nav>
36
+ {% endif %}
37
+ </div>
38
+ </header>
39
+
40
+ <main>
41
+ {{ content }}
42
+ </main>
43
+
44
+ <footer class="bb-footer">
45
+ <div class="bb-wrap bb-footer-inner">
46
+ {% if globals.company %}
47
+ <div class="bb-footer-info">
48
+ {% if globals.company.address %}<p>{{ globals.company.address }}</p>{% endif %}
49
+ {% if globals.company.phone %}<p>{{ globals.company.phone }}</p>{% endif %}
50
+ </div>
51
+ {% endif %}
52
+ {% if globals.social %}
53
+ <div class="bb-footer-social">
54
+ {% if globals.social.instagram %}<a href="{{ globals.social.instagram }}">Instagram</a>{% endif %}
55
+ {% if globals.social.facebook %}<a href="{{ globals.social.facebook }}">Facebook</a>{% endif %}
56
+ </div>
57
+ {% endif %}
58
+ {% unless site.theme_settings.show_powered_by == false %}
59
+ <p class="bb-powered"><a href="{{ site.powered_by_url }}">Powered by {{ site.powered_by_name }}</a></p>
60
+ {% endunless %}
61
+ </div>
62
+ </footer>
63
+ </body>
64
+ </html>
@@ -0,0 +1,32 @@
1
+ <div class="bb-wrap bb-collection">
2
+ <h1>{{ collection.title }}</h1>
3
+
4
+ {% for post in collection.entries %}
5
+ <article class="bb-post-card">
6
+ {% if post.data.hero_image.url %}
7
+ <a href="{{ post.url }}">
8
+ <img class="bb-post-card-image" src="{{ post.data.hero_image.url }}" alt="{{ post.data.hero_image.alt_text | default: post.title }}">
9
+ </a>
10
+ {% endif %}
11
+ <h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
12
+ {% if post.published_at %}
13
+ <p class="bb-post-meta">{{ post.published_at | date: "%B %-d, %Y" }}</p>
14
+ {% endif %}
15
+ {% if post.data.excerpt %}
16
+ <p>{{ post.data.excerpt }}</p>
17
+ {% endif %}
18
+ </article>
19
+ {% endfor %}
20
+
21
+ {% if collection.entries.size == 0 %}
22
+ <p>No posts yet.</p>
23
+ {% endif %}
24
+
25
+ {% if pagination.total_pages > 1 %}
26
+ <nav class="bb-pagination">
27
+ {% if pagination.previous_url %}<a href="{{ pagination.previous_url }}">&larr; Previous</a>{% endif %}
28
+ <span>Page {{ pagination.current_page }} of {{ pagination.total_pages }}</span>
29
+ {% if pagination.next_url %}<a href="{{ pagination.next_url }}">Next &rarr;</a>{% endif %}
30
+ </nav>
31
+ {% endif %}
32
+ </div>
@@ -0,0 +1,13 @@
1
+ <article class="bb-wrap bb-entry">
2
+ <h1>{{ entry.title }}</h1>
3
+
4
+ {% assign body_html = entry.data.body | markdown | strip %}
5
+ {% if body_html != "" %}
6
+ <div class="prose">{{ body_html }}</div>
7
+ {% endif %}
8
+
9
+ {% assign sections_html = entry.data.sections | strip %}
10
+ {% if sections_html != "" %}
11
+ {{ sections_html }}
12
+ {% endif %}
13
+ </article>
@@ -0,0 +1,4 @@
1
+ <article class="bb-wrap bb-entry">
2
+ {{ entry.data.sections }}
3
+ </article>
4
+ <p>THIS IS LANDING.LIQUID</p>
@@ -0,0 +1,18 @@
1
+ <article class="bb-wrap bb-entry">
2
+ <h1>{{ entry.title }}</h1>
3
+
4
+ {% if entry.data.hero_image.url %}
5
+ <img class="bb-entry-image" src="{{ entry.data.hero_image.url }}" alt="{{ entry.data.hero_image.alt_text | default: entry.title }}">
6
+ {% endif %}
7
+
8
+ {% assign body_html = entry.data.body | markdown | strip %}
9
+ {% if body_html != "" %}
10
+ <div class="prose">{{ body_html }}</div>
11
+ {% endif %}
12
+
13
+ {% comment %} sections is pre-rendered block HTML (empty string when no blocks) {% endcomment %}
14
+ {% assign sections_html = entry.data.sections | strip %}
15
+ {% if sections_html != "" %}
16
+ {{ sections_html }}
17
+ {% endif %}
18
+ </article>
@@ -0,0 +1,8 @@
1
+ <article class="bb-wrap bb-entry bb-post">
2
+ <h1>{{ entry.title }}</h1>
3
+ {% if entry.published_at %}<p class="bb-post-meta">{{ entry.published_at | date: "%B %-d, %Y" }}</p>{% endif %}
4
+ {% if entry.data.hero_image.url %}
5
+ <img class="bb-entry-image" src="{{ entry.data.hero_image.url }}" alt="{{ entry.data.hero_image.alt_text | default: entry.title }}">
6
+ {% endif %}
7
+ <div class="prose">{{ entry.data.body | markdown }}</div>
8
+ </article>
@@ -0,0 +1,10 @@
1
+ {% comment %}
2
+ Fallback shown at "/" only when no page is slugged "home".
3
+ Normally the homepage is the page with slug "home", rendered through its own
4
+ entry template (e.g. entries/pages.liquid).
5
+ {% endcomment %}
6
+ <section class="bb-wrap bb-hero-fallback">
7
+ <h1>{{ site.name }}</h1>
8
+ {% if site.tagline %}<p>{{ site.tagline }}</p>{% endif %}
9
+ <p class="bb-empty-note">Create a page with the slug “home” to build your homepage.</p>
10
+ </section>
@@ -0,0 +1,28 @@
1
+ <div class="bb-wrap bb-collection">
2
+ <h1>Search</h1>
3
+
4
+ <form action="/search" method="get" style="margin-bottom: 2rem;">
5
+ <div style="display: flex; gap: 0.5rem;">
6
+ <input type="text" name="q" value="{{ search.query }}" placeholder="Search…"
7
+ style="flex: 1; padding: 0.5rem 0.75rem; border: 1px solid var(--bb-cinnamon); border-radius: 0.375rem; font-family: 'Nunito Sans', sans-serif;">
8
+ <button type="submit" style="padding: 0.5rem 1rem; background: var(--bb-poppy); color: white; border: none; border-radius: 0.375rem; cursor: pointer; font-family: 'Lilita One', cursive;">Search</button>
9
+ </div>
10
+ </form>
11
+
12
+ {% if search.query != "" %}
13
+ <p class="bb-post-meta">{{ search.total }} result{% if search.total != 1 %}s{% endif %} for "{{ search.query }}"</p>
14
+
15
+ {% for entry in search.results %}
16
+ <article class="bb-post-card">
17
+ <h2><a href="{{ entry.url }}">{{ entry.title }}</a></h2>
18
+ {% if entry.published_at %}
19
+ <p class="bb-post-meta">{{ entry.published_at | date: "%B %-d, %Y" }}</p>
20
+ {% endif %}
21
+ </article>
22
+ {% endfor %}
23
+
24
+ {% if search.total == 0 %}
25
+ <p>No results found.</p>
26
+ {% endif %}
27
+ {% endif %}
28
+ </div>
@@ -0,0 +1,47 @@
1
+ name: Bagel Boy
2
+ handle: bagel-boy
3
+ version: 1.0.0
4
+ author: Bagel Boy
5
+ category: Restaurant
6
+ screenshot: screenshot.svg
7
+ description: Retro neighborhood-bakery theme for Bagel Boy.
8
+ settings:
9
+ fields:
10
+ - handle: accent_color
11
+ type: color
12
+ label: Accent Color
13
+ default: "#FB404C"
14
+ help: Blood Burst — buttons, links, accents.
15
+ - handle: secondary_color
16
+ type: color
17
+ label: Secondary Color
18
+ default: "#FDC694"
19
+ help: Blushing Cinnamon — soft panels and highlights.
20
+ - handle: show_powered_by
21
+ type: boolean
22
+ label: Show Powered By
23
+ default: true
24
+ # Bagel Boy adds restaurant-specific blocks on top of Plum's base blocks.
25
+ blocks:
26
+ - handle: menu_item
27
+ label: Menu Item
28
+ fields:
29
+ - handle: name
30
+ type: text
31
+ label: Name
32
+ - handle: price
33
+ type: text
34
+ label: Price
35
+ - handle: description
36
+ type: textarea
37
+ label: Description
38
+ - handle: hours
39
+ label: Hours
40
+ fields:
41
+ - handle: heading
42
+ type: text
43
+ label: Heading
44
+ - handle: schedule
45
+ type: textarea
46
+ label: Schedule
47
+ help: One line per day, e.g. "Mon–Fri 6a–2p"
@@ -0,0 +1,28 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 750" role="img" aria-labelledby="title desc">
2
+ <title id="title">Bagel Shop theme screenshot</title>
3
+ <desc id="desc">A warm storefront website layout with a large headline and bagel illustration.</desc>
4
+ <rect width="1200" height="750" fill="#fffaf0"/>
5
+ <rect x="94" y="76" width="1012" height="598" rx="18" fill="#fff7e8" stroke="#2b2119" stroke-opacity=".16" stroke-width="4"/>
6
+ <rect x="94" y="76" width="1012" height="96" rx="18" fill="#fff7e8"/>
7
+ <line x1="94" y1="172" x2="1106" y2="172" stroke="#2b2119" stroke-opacity=".16" stroke-width="4"/>
8
+ <circle cx="158" cy="124" r="34" fill="#b9652d"/>
9
+ <circle cx="158" cy="124" r="13" fill="#fffaf0"/>
10
+ <circle cx="146" cy="111" r="4" fill="#f7d58a"/>
11
+ <circle cx="170" cy="116" r="4" fill="#f7d58a"/>
12
+ <rect x="210" y="104" width="206" height="24" rx="12" fill="#2b2119"/>
13
+ <rect x="210" y="139" width="154" height="13" rx="6.5" fill="#765f4b"/>
14
+ <rect x="888" y="114" width="124" height="16" rx="8" fill="#1f6f63"/>
15
+ <text x="150" y="307" fill="#1f6f63" font-family="Arial, sans-serif" font-size="25" font-weight="700">FRESH FROM THE OVEN</text>
16
+ <rect x="150" y="338" width="438" height="78" rx="10" fill="#2b2119"/>
17
+ <rect x="150" y="434" width="350" height="78" rx="10" fill="#2b2119"/>
18
+ <rect x="150" y="548" width="438" height="20" rx="10" fill="#765f4b"/>
19
+ <rect x="150" y="584" width="374" height="20" rx="10" fill="#765f4b"/>
20
+ <rect x="744" y="250" width="250" height="304" rx="12" fill="#f5e7cf" stroke="#2b2119" stroke-opacity=".16" stroke-width="4" transform="rotate(2 869 402)"/>
21
+ <circle cx="870" cy="380" r="104" fill="#b9652d"/>
22
+ <circle cx="870" cy="380" r="32" fill="#f5e7cf"/>
23
+ <circle cx="834" cy="338" r="8" fill="#f9db8d"/>
24
+ <circle cx="895" cy="333" r="7" fill="#f9db8d"/>
25
+ <circle cx="918" cy="394" r="8" fill="#f9db8d"/>
26
+ <circle cx="850" cy="438" r="7" fill="#f9db8d"/>
27
+ <rect x="784" y="506" width="172" height="15" rx="7.5" fill="#765f4b"/>
28
+ </svg>
@@ -0,0 +1,7 @@
1
+ html {
2
+ color-scheme: light;
3
+ }
4
+
5
+ .hero-card {
6
+ will-change: transform;
7
+ }