jekyll-stepper 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7c0671026469134aa7f18902648e87a6292a1429775fe781461fce71771d9a63
4
+ data.tar.gz: e7ef7b95f2934dd92d90291b792fceaf0fe951736083255143a468fd5fbf83ab
5
+ SHA512:
6
+ metadata.gz: 22aa1374ab61abecfc81236f9c409088b313e956e6f597f84ff8a5a81c568f4e6e48bb5a235b49eef4903032c10bd75911e297227b9220fcd184949e2d231087
7
+ data.tar.gz: 107fe0bc78215a87ca8f7e0b755cfe0e6c7737dbf7fdfaa63907fbb6edbf532c8d55f97c6c1c1d42d22a1f0d43d06077d141630db876518359362af0ac240d86
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Jean-Francois Morin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,167 @@
1
+ # Jekyll Stepper
2
+
3
+ A Jekyll plugin for step-by-step tutorial accordions. Converts fenced code blocks with the `stepper` language identifier into interactive accordion components with Previous/Next navigation.
4
+
5
+ ## Installation
6
+
7
+ Add to your `Gemfile`:
8
+
9
+ ```ruby
10
+ gem "jekyll-stepper"
11
+ ```
12
+
13
+ Then run:
14
+
15
+ ```bash
16
+ bundle install
17
+ ```
18
+
19
+ Add to your `_config.yml`:
20
+
21
+ ```yaml
22
+ plugins:
23
+ - jekyll-stepper
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ Use a fenced code block with `stepper` as the language identifier:
29
+
30
+ ````markdown
31
+ ````stepper
32
+ # Step 1: First step title
33
+
34
+ Content for the first step in markdown.
35
+
36
+ # Step 2: Second step title
37
+
38
+ Content for the second step, including code blocks:
39
+
40
+ ```html
41
+ <h1>Hello World</h1>
42
+ ```
43
+
44
+ # Step 3: Final step
45
+
46
+ More content here.
47
+ ````
48
+ ````
49
+
50
+ ### Syntax rules
51
+
52
+ - Use **4 backticks** for the outer block to allow nested code blocks inside
53
+ - Each `# Title` becomes an accordion header
54
+ - Content between titles is rendered as Markdown
55
+ - A navigation bar with Previous/Next buttons is added automatically
56
+ - The first step is open by default
57
+
58
+ ## Styling
59
+
60
+ The plugin generates HTML with these CSS classes:
61
+
62
+ - `.stepper` — Container wrapper
63
+ - `.stepper-nav` — Navigation bar
64
+ - `.stepper-prev` / `.stepper-next` — Navigation buttons
65
+ - `.stepper-progress` — Step counter
66
+ - `details` / `summary` — Accordion elements
67
+
68
+ If your theme doesn't include stepper styles, add this to your CSS:
69
+
70
+ ```css
71
+ .stepper {
72
+ margin-bottom: 2rem;
73
+ }
74
+
75
+ .stepper details {
76
+ border: 1px solid var(--color-border, #E5E8EA);
77
+ border-radius: 8px;
78
+ margin-bottom: 0.5rem;
79
+ overflow: hidden;
80
+ }
81
+
82
+ .stepper details summary {
83
+ padding: 1rem 2rem;
84
+ cursor: pointer;
85
+ font-weight: 500;
86
+ background: var(--color-code-bg, #F4F6F8);
87
+ transition: background 0.2s;
88
+ list-style: none;
89
+ }
90
+
91
+ .stepper details summary::-webkit-details-marker {
92
+ display: none;
93
+ }
94
+
95
+ .stepper details summary::before {
96
+ content: "\25B8";
97
+ margin-right: 0.5rem;
98
+ transition: transform 0.2s;
99
+ display: inline-block;
100
+ }
101
+
102
+ .stepper details[open] summary::before {
103
+ transform: rotate(90deg);
104
+ }
105
+
106
+ .stepper details[open] summary {
107
+ background: var(--color-border, #E5E8EA);
108
+ border-left: 3px solid var(--color-red, #E40046);
109
+ }
110
+
111
+ .stepper details summary:hover {
112
+ background: var(--color-border, #E5E8EA);
113
+ }
114
+
115
+ .stepper .stepper-content {
116
+ padding: 1rem 2rem;
117
+ }
118
+
119
+ .stepper .stepper-nav {
120
+ display: flex;
121
+ align-items: center;
122
+ justify-content: space-between;
123
+ gap: 1rem;
124
+ padding-top: 1rem;
125
+ margin-top: 1rem;
126
+ border-top: 1px solid var(--color-border, #E5E8EA);
127
+ }
128
+
129
+ .stepper-prev,
130
+ .stepper-next {
131
+ padding: 0.5rem 1rem;
132
+ border: 1px solid var(--color-border, #E5E8EA);
133
+ border-radius: 6px;
134
+ background: var(--color-bg, #FFFFFF);
135
+ color: var(--color-text, #1A1A1A);
136
+ font-size: 0.875rem;
137
+ cursor: pointer;
138
+ transition: opacity 0.2s, background 0.2s;
139
+ }
140
+
141
+ .stepper-prev:hover:not(:disabled),
142
+ .stepper-next:hover:not(:disabled) {
143
+ background: var(--color-border, #E5E8EA);
144
+ }
145
+
146
+ .stepper-prev:disabled,
147
+ .stepper-next:disabled {
148
+ opacity: 0.4;
149
+ cursor: not-allowed;
150
+ }
151
+
152
+ .stepper-progress {
153
+ font-size: 0.875rem;
154
+ color: var(--color-text-secondary, #5A6A72);
155
+ }
156
+ ```
157
+
158
+ ## Development
159
+
160
+ ```bash
161
+ gem build jekyll-stepper.gemspec
162
+ gem install jekyll-stepper-0.1.0.gem
163
+ ```
164
+
165
+ ## License
166
+
167
+ MIT
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/jekyll-stepper/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "jekyll-stepper"
7
+ spec.version = Jekyll::Stepper::VERSION
8
+ spec.authors = ["Jean-Francois Morin"]
9
+ spec.email = ["jmorin@simplon.co"]
10
+
11
+ spec.summary = "A Jekyll plugin for step-by-step tutorial accordions"
12
+ spec.description = "Converts fenced code blocks with the 'stepper' language identifier into interactive accordion components with navigation for step-by-step tutorials."
13
+ spec.homepage = "https://github.com/jmorin-simplon/jekyll-stepper"
14
+ spec.license = "MIT"
15
+
16
+ spec.required_ruby_version = ">= 2.7.0"
17
+
18
+ spec.files = `git ls-files -z`.split("\x0").reject { |f|
19
+ f.match(%r!^(test|spec|features|\.github)/!)
20
+ }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_dependency "jekyll", ">= 3.9"
24
+ spec.add_dependency "kramdown-parser-gfm", ">= 1.0"
25
+
26
+ if spec.respond_to?(:metadata)
27
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
28
+ spec.metadata["rubygems_mfa_required"] = "true"
29
+ end
30
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Stepper
5
+ class Converter
6
+ def initialize(config)
7
+ @config = config
8
+ end
9
+
10
+ def convert(content)
11
+ lines = content.lines
12
+ result = []
13
+ i = 0
14
+
15
+ while i < lines.length
16
+ line = lines[i]
17
+ match = line.match(/^(`{3,})(\S*)\s*$/)
18
+
19
+ if match
20
+ backtick_count = match[1].length
21
+ info_string = match[2]
22
+
23
+ if info_string == "stepper"
24
+ block_lines = []
25
+ i += 1
26
+ closer = /^`{#{backtick_count}}\s*$/
27
+
28
+ while i < lines.length
29
+ break if lines[i].match(closer)
30
+ block_lines << lines[i]
31
+ i += 1
32
+ end
33
+
34
+ i += 1 if i < lines.length
35
+
36
+ steps = parse_steps(block_lines.join)
37
+ if steps.empty?
38
+ result << "#{match[1]}stepper\n"
39
+ result.concat(block_lines)
40
+ result << "`" * backtick_count + "\n"
41
+ else
42
+ result << render_stepper(steps)
43
+ end
44
+ else
45
+ result << line
46
+ i += 1
47
+ closer = /^`{#{backtick_count}}\s*$/
48
+
49
+ while i < lines.length
50
+ result << lines[i]
51
+ break if lines[i].match(closer)
52
+ i += 1
53
+ end
54
+
55
+ i += 1
56
+ end
57
+ else
58
+ result << line
59
+ i += 1
60
+ end
61
+ end
62
+
63
+ result.join
64
+ end
65
+
66
+ private
67
+
68
+ def parse_steps(content)
69
+ steps = []
70
+ current_title = nil
71
+ current_content = []
72
+
73
+ content.lines.each do |line|
74
+ if (match = line.match(/^#\s+(.+)$/))
75
+ steps << { title: current_title, content: current_content.join } if current_title
76
+ current_title = match[1].strip
77
+ current_content = []
78
+ else
79
+ current_content << line
80
+ end
81
+ end
82
+
83
+ steps << { title: current_title, content: current_content.join } if current_title
84
+ steps
85
+ end
86
+
87
+ def render_stepper(steps)
88
+ step_count = steps.length
89
+ html = %(<div class="stepper" data-steps="#{step_count}">\n)
90
+
91
+ steps.each_with_index do |step, index|
92
+ html << %( <hr />\n) if index > 0
93
+ html << %( <details#{index.zero? ? " open" : ""}>\n)
94
+ html << %( <summary>#{step[:title]}</summary>\n)
95
+ html << %( <div class="stepper-content">\n)
96
+
97
+ html << render_markdown(step[:content])
98
+
99
+ html << %( <div class="stepper-nav">\n)
100
+ html << %( <button class="stepper-prev"#{index.zero? ? " disabled" : ""}>\u2190 Pr\u00e9c\u00e9dent</button>\n)
101
+ html << %( <span class="stepper-progress">\u00c9tape #{index + 1} / #{step_count}</span>\n)
102
+ html << %( <button class="stepper-next"#{index == step_count - 1 ? " disabled" : ""}>Suivant \u2192</button>\n)
103
+ html << %( </div>\n)
104
+ html << %( </div>\n)
105
+ html << %( </details>\n)
106
+ end
107
+
108
+ html << %(</div>\n)
109
+ html
110
+ end
111
+
112
+ def render_markdown(content)
113
+ return "" if content.strip.empty?
114
+
115
+ doc = Kramdown::Document.new(content, input: "GFM")
116
+ doc.to_html
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Stepper
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jekyll"
4
+ require "jekyll-stepper/version"
5
+ require "jekyll-stepper/converter"
6
+
7
+ module Jekyll
8
+ module Stepper
9
+ end
10
+ end
11
+
12
+ Jekyll::Hooks.register [:pages, :posts, :documents], :pre_render do |doc|
13
+ converter = Jekyll::Stepper::Converter.new(doc.site.config)
14
+ doc.content = converter.convert(doc.content)
15
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jekyll-stepper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jean-Francois Morin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-08-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jekyll
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '3.9'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '3.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: kramdown-parser-gfm
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ description: Converts fenced code blocks with the 'stepper' language identifier into
42
+ interactive accordion components with navigation for step-by-step tutorials.
43
+ email:
44
+ - jmorin@simplon.co
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".gitignore"
50
+ - Gemfile
51
+ - LICENSE.txt
52
+ - README.md
53
+ - jekyll-stepper.gemspec
54
+ - lib/jekyll-stepper.rb
55
+ - lib/jekyll-stepper/converter.rb
56
+ - lib/jekyll-stepper/version.rb
57
+ homepage: https://github.com/jmorin-simplon/jekyll-stepper
58
+ licenses:
59
+ - MIT
60
+ metadata:
61
+ allowed_push_host: https://rubygems.org
62
+ rubygems_mfa_required: 'true'
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: 2.7.0
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubygems_version: 3.5.23
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: A Jekyll plugin for step-by-step tutorial accordions
82
+ test_files: []