social_login_buttons 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 +7 -0
- data/LICENSE +22 -0
- data/README.md +74 -0
- data/app/assets/stylesheets/social_login_buttons.css +59 -0
- data/lib/social_login_buttons/data/logos.json +5 -0
- data/lib/social_login_buttons/data/tokens.json +382 -0
- data/lib/social_login_buttons/helper.rb +8 -0
- data/lib/social_login_buttons/railtie.rb +16 -0
- data/lib/social_login_buttons/renderer.rb +137 -0
- data/lib/social_login_buttons/version.rb +3 -0
- data/lib/social_login_buttons.rb +11 -0
- metadata +50 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 2292c0fb0d3070a21f520435e4e03d3f7188f20372b463f1a646d1f084dbf8bd
|
|
4
|
+
data.tar.gz: bd8f5ce236d1efdcb4eb1a8efe0aa921a203f086a22f6357b9e094dad94b276f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 20337a14821d7336087fc883c278c7ea46eb94061e47ed440404f26fc938e106935f20b7877d4d955a6716922d7ad074022d293e57067c3df0dd3cd134079465
|
|
7
|
+
data.tar.gz: 32d0485dd06ab014e416d15f64861edf57b33a06553c61acc8cafec8530673fb898caeb7b6b5d610f4484f8779b476f27ef99355dbf33a956bcd3598b27e453f
|
data/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dico (AI-Dico)
|
|
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 all
|
|
13
|
+
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 THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
data/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# social_login_buttons
|
|
2
|
+
|
|
3
|
+
Version 0.1.0 · Ruby >=3.1 · no runtime dependencies beyond standard-library JSON.
|
|
4
|
+
Kakao, NAVER, Apple and Google buttons with HTML identical to
|
|
5
|
+
`@ai-dico/social-login-buttons` for the same options.
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
gem 'social_login_buttons', '~> 0.1.0'
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```erb
|
|
12
|
+
<%= stylesheet_link_tag 'social_login_buttons' %>
|
|
13
|
+
<%= social_login_button :kakao, purpose: :continue %>
|
|
14
|
+
<%= social_login_button :naver, type: :submit, attrs: { 'name' => 'provider', 'value' => 'naver' } %>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The Railtie registers the helper with ActionView and the stylesheet directory with
|
|
18
|
+
an available Rails asset pipeline. No Rails installation is needed for plain Ruby:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
require 'social_login_buttons'
|
|
22
|
+
html = SocialLoginButtons.render(provider: :naver, lang: :en)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Plain render returns a String. Only the helper calls `html_safe` when available.
|
|
26
|
+
Options accept symbol or string keys and values, and both `logoColor`/`logo_color`
|
|
27
|
+
and `appleLogoSrc`/`apple_logo_src`. Attribute maps use string keys and string values.
|
|
28
|
+
|
|
29
|
+
| Option | Values | Default |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| provider | kakao, naver, apple, google | required |
|
|
32
|
+
| purpose | login, continue, signup, short | continue |
|
|
33
|
+
| lang | ko, en | ko |
|
|
34
|
+
| tone | brand, white, black, custom | brand |
|
|
35
|
+
| theme | light, dark | light |
|
|
36
|
+
| corner | official, 0, 4, 8, 12, pill | official |
|
|
37
|
+
| height | official or a positive finite pixel number | 48 |
|
|
38
|
+
| font | unified, brand | unified |
|
|
39
|
+
| logoColor | brand, mono | brand |
|
|
40
|
+
| layout | full, icon | full |
|
|
41
|
+
| align | center, logo-left | center |
|
|
42
|
+
| bg, fg, border | #RRGGBB; bg/fg required for custom, border optional | none |
|
|
43
|
+
| appleLogoSrc | URL to your own official Apple logo file | no logo (icon layout shows the text "Apple") |
|
|
44
|
+
| type | button, submit | button |
|
|
45
|
+
| attrs | string attribute names and string values | none |
|
|
46
|
+
|
|
47
|
+
Attributes are sorted by ASCII name after canonical attributes and all values escape
|
|
48
|
+
`& < > " '`. Invalid options, invalid attribute names, event handler attributes and
|
|
49
|
+
overrides of `type`, `class`, `style`, `aria-label` raise `ArgumentError`.
|
|
50
|
+
For boolean HTML attributes use e.g. `attrs: { 'disabled' => '' }`.
|
|
51
|
+
|
|
52
|
+
Official height resolves to the provider's web height; unified text always uses
|
|
53
|
+
`max(14, round(height / 3))`, weight 500. Brand text uses the official size or rounded
|
|
54
|
+
height ratio and weight. Explicit corners are applied without adjustment.
|
|
55
|
+
Custom borders use background/page contrast <1.3; an explicit border overrides it.
|
|
56
|
+
Custom brand logos choose the white-tone palette at relative luminance >0.5.
|
|
57
|
+
The `.slb-stack` and `.slb-row` classes arrange groups. Override the system font with
|
|
58
|
+
`.slb { font-family: inherit; }` after the stylesheet. OAuth is the application's responsibility.
|
|
59
|
+
|
|
60
|
+
Apple artwork is not distributed. Get the official file from
|
|
61
|
+
[Apple Design Resources](https://developer.apple.com/design/resources/#sign-in-with-apple)
|
|
62
|
+
and supply `apple_logo_src`. Files are never tinted; missing URLs produce no logo slot,
|
|
63
|
+
so full-layout text stays centered (icon layout shows the short text "Apple" instead of an empty square).
|
|
64
|
+
|
|
65
|
+
Regenerate from the repository root: `node tools/gen-packages.mjs`.
|
|
66
|
+
Test without Rails:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
ruby -Ilib -Itest -e 'Dir["test/*_test.rb"].each{|f| require File.expand_path(f)}'
|
|
70
|
+
gem build social_login_buttons.gemspec
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Code/tokens/docs: MIT. Provider logos and names are their respective owners' trademarks
|
|
74
|
+
and are excluded from the MIT grant. This package is not affiliated with those companies.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/* Generated by tools/gen-packages.mjs from tokens.json. */
|
|
2
|
+
.slb {
|
|
3
|
+
display: inline-flex;
|
|
4
|
+
position: relative;
|
|
5
|
+
box-sizing: border-box;
|
|
6
|
+
align-items: center;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
height: var(--slb-h);
|
|
9
|
+
border-radius: var(--slb-r);
|
|
10
|
+
font-family: -apple-system, "Apple SD Gothic Neo", Roboto, sans-serif;
|
|
11
|
+
font-size: var(--slb-fs);
|
|
12
|
+
font-weight: var(--slb-fw);
|
|
13
|
+
line-height: 1.2;
|
|
14
|
+
background: var(--slb-bg);
|
|
15
|
+
color: var(--slb-fg);
|
|
16
|
+
box-shadow: inset 0 0 0 1px var(--slb-bd);
|
|
17
|
+
border: 0;
|
|
18
|
+
cursor: pointer;
|
|
19
|
+
white-space: nowrap;
|
|
20
|
+
gap: 8px;
|
|
21
|
+
padding: 0 16px;
|
|
22
|
+
word-break: keep-all;
|
|
23
|
+
text-decoration: none;
|
|
24
|
+
vertical-align: middle;
|
|
25
|
+
}
|
|
26
|
+
.slb--full { width: 100%; }
|
|
27
|
+
.slb--icon { width: var(--slb-h); padding: 0; flex-shrink: 0; }
|
|
28
|
+
.slb--logo-left .slb__logo { position: absolute; left: 16px; }
|
|
29
|
+
.slb--icon.slb--logo-left .slb__logo { position: static; }
|
|
30
|
+
.slb__logo {
|
|
31
|
+
display: inline-flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
justify-content: center;
|
|
34
|
+
flex: 0 0 auto;
|
|
35
|
+
color: var(--slb-logo);
|
|
36
|
+
width: var(--slb-logo-size);
|
|
37
|
+
height: var(--slb-logo-size);
|
|
38
|
+
}
|
|
39
|
+
.slb__logo > svg, .slb__logo > img { display: block; width: 100%; height: 100%; flex-shrink: 0; }
|
|
40
|
+
.slb__logo > img { object-fit: contain; }
|
|
41
|
+
.slb__label { min-width: 0; }
|
|
42
|
+
.slb__label--short { font-size: calc(var(--slb-h) * 0.28); }
|
|
43
|
+
.slb--kakao { --slb-logo-size: calc(var(--slb-h) * 0.3); }
|
|
44
|
+
.slb--kakao.slb--icon { --slb-logo-size: calc(var(--slb-h) * 0.46); }
|
|
45
|
+
.slb--naver { --slb-logo-size: 16px; }
|
|
46
|
+
.slb--naver.slb--logo-left { --slb-logo-size: 18px; }
|
|
47
|
+
.slb--naver.slb--icon { --slb-logo-size: 18px; }
|
|
48
|
+
.slb--google { --slb-logo-size: 20px; }
|
|
49
|
+
.slb--apple { --slb-logo-size: var(--slb-h); }
|
|
50
|
+
/* Shift the original out of the clipped box; its zero-blur shadow uses fg exactly. */
|
|
51
|
+
.slb--google.slb--logo-mono .slb__logo { overflow: hidden; }
|
|
52
|
+
.slb--google.slb--logo-mono .slb__logo > img {
|
|
53
|
+
transform: translateX(-100%);
|
|
54
|
+
filter: drop-shadow(var(--slb-logo-size) 0 0 var(--slb-fg));
|
|
55
|
+
}
|
|
56
|
+
.slb-stack { display: flex; flex-direction: column; gap: 10px; }
|
|
57
|
+
.slb-row { display: flex; flex-direction: row; align-items: center; gap: 10px; }
|
|
58
|
+
.slb:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }
|
|
59
|
+
.slb:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kakao": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"13 13 22 22\" width=\"22\" height=\"22\"><path fill=\"currentColor\" d=\"M24.0014 14C17.9241 14 13 18.0219 13 22.9825C13 26.1711 15.0368 28.9728 18.1057 30.5656L17.0681 34.5677C17.0295 34.6871 17.0598 34.8151 17.1424 34.9033C17.2029 34.9659 17.2855 35 17.3653 35C17.4341 35 17.5029 34.9772 17.5607 34.9289L22.0196 31.8171C22.661 31.911 23.3215 31.9622 23.9986 31.9622C30.0732 31.9622 35 27.9403 35 22.9797C35 18.0191 30.0759 14 24.0014 14Z\"/></svg>",
|
|
3
|
+
"naver": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 72 72\" width=\"18\" height=\"18\"><g transform=\"translate(-60,132) scale(0.1,-0.1)\"><path fill=\"currentColor\" d=\"M600 960 l0 -360 115 0 115 0 2 193 3 192 133 -192 134 -193 109 0 109 0 0 360 0 360 -115 0 -115 0 -2 -192 -3 -193 -133 193 -134 192 -109 0 -109 0 0 -360z\"/></g></svg>",
|
|
4
|
+
"google": "data:image/png;base64,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"
|
|
5
|
+
}
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema_note": "소셜 로그인 버튼 디자인 토큰 정본. 값 출처: docs/research/official-guides-2026-11-05.md",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"order": [
|
|
5
|
+
"kakao",
|
|
6
|
+
"naver",
|
|
7
|
+
"apple",
|
|
8
|
+
"google"
|
|
9
|
+
],
|
|
10
|
+
"defaults": {
|
|
11
|
+
"height": 48,
|
|
12
|
+
"radius": "official",
|
|
13
|
+
"gap": 10,
|
|
14
|
+
"font": {
|
|
15
|
+
"mode": "unified",
|
|
16
|
+
"size": 16,
|
|
17
|
+
"weight": 500,
|
|
18
|
+
"rule": "size = max(14, round(height / 3))",
|
|
19
|
+
"note": "unified 는 일관성을 위해 모든 버튼을 같은 크기로 맞춘다 — Apple 공식(높이의 43%)·Google 공식(14px)과 다르다. 공식 크기는 font.mode = brand"
|
|
20
|
+
},
|
|
21
|
+
"tone": "brand",
|
|
22
|
+
"logoColor": "brand",
|
|
23
|
+
"layout": "full-width-stack",
|
|
24
|
+
"align": "center",
|
|
25
|
+
"purpose": "auto"
|
|
26
|
+
},
|
|
27
|
+
"options": {
|
|
28
|
+
"radius": [
|
|
29
|
+
"official",
|
|
30
|
+
0,
|
|
31
|
+
4,
|
|
32
|
+
8,
|
|
33
|
+
12,
|
|
34
|
+
"pill"
|
|
35
|
+
],
|
|
36
|
+
"radiusRule": "고른 값을 모든 회사에 똑같이 적용한다. 회사별 보정 없음. pill = height / 2",
|
|
37
|
+
"height": [
|
|
38
|
+
"official",
|
|
39
|
+
44,
|
|
40
|
+
48,
|
|
41
|
+
52,
|
|
42
|
+
56
|
|
43
|
+
],
|
|
44
|
+
"tone": [
|
|
45
|
+
"brand",
|
|
46
|
+
"white",
|
|
47
|
+
"black",
|
|
48
|
+
"custom"
|
|
49
|
+
],
|
|
50
|
+
"logoColor": [
|
|
51
|
+
"brand",
|
|
52
|
+
"mono"
|
|
53
|
+
],
|
|
54
|
+
"layout": [
|
|
55
|
+
"full-width-stack",
|
|
56
|
+
"narrow",
|
|
57
|
+
"icon-row",
|
|
58
|
+
"primary-plus-icons"
|
|
59
|
+
],
|
|
60
|
+
"align": [
|
|
61
|
+
"center",
|
|
62
|
+
"logo-left"
|
|
63
|
+
],
|
|
64
|
+
"purpose": [
|
|
65
|
+
"auto",
|
|
66
|
+
"login",
|
|
67
|
+
"continue",
|
|
68
|
+
"signup"
|
|
69
|
+
],
|
|
70
|
+
"lang": [
|
|
71
|
+
"ko",
|
|
72
|
+
"en"
|
|
73
|
+
],
|
|
74
|
+
"purposeRule": "auto = 화면에 맞게 고른다 — 가입 화면 signup, 로그인 화면 login, 둘을 겸하면 continue. 한 화면 안의 버튼은 같은 종류로 맞춘다"
|
|
75
|
+
},
|
|
76
|
+
"tone": {
|
|
77
|
+
"white": {
|
|
78
|
+
"light": {
|
|
79
|
+
"bg": "#FFFFFF",
|
|
80
|
+
"fg": "#1F1F1F",
|
|
81
|
+
"border": "#DADCE0"
|
|
82
|
+
},
|
|
83
|
+
"dark": {
|
|
84
|
+
"bg": "#FFFFFF",
|
|
85
|
+
"fg": "#1F1F1F",
|
|
86
|
+
"border": null
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"black": {
|
|
90
|
+
"light": {
|
|
91
|
+
"bg": "#000000",
|
|
92
|
+
"fg": "#FFFFFF",
|
|
93
|
+
"border": null
|
|
94
|
+
},
|
|
95
|
+
"dark": {
|
|
96
|
+
"bg": "#000000",
|
|
97
|
+
"fg": "#FFFFFF",
|
|
98
|
+
"border": "#3A3A3A"
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"custom": {
|
|
102
|
+
"rule": "bg·fg 를 사용자가 정한다. 글자 대비 4.5:1 이상 권장(WCAG AA). bg 가 화면 배경과 대비 1.3 미만이면 border = 흰 화면 #DADCE0 / 검정 화면 #3A3A3A"
|
|
103
|
+
},
|
|
104
|
+
"brandLogoOnTone": {
|
|
105
|
+
"kakao": {
|
|
106
|
+
"white": "#191919",
|
|
107
|
+
"black": "#FEE500"
|
|
108
|
+
},
|
|
109
|
+
"naver": {
|
|
110
|
+
"white": "#03A94D",
|
|
111
|
+
"black": "#05AC4F"
|
|
112
|
+
},
|
|
113
|
+
"apple": {
|
|
114
|
+
"white": "#000000",
|
|
115
|
+
"black": "#FFFFFF"
|
|
116
|
+
},
|
|
117
|
+
"google": {
|
|
118
|
+
"white": "full-color",
|
|
119
|
+
"black": "full-color"
|
|
120
|
+
},
|
|
121
|
+
"_rule": "tone 이 white/black/custom 일 때만 쓴다. tone=brand 면 providers.<회사>.brand.<theme>.logo (없으면 그 fg, Google G 는 원래 색)"
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"providers": {
|
|
125
|
+
"kakao": {
|
|
126
|
+
"name": {
|
|
127
|
+
"ko": "카카오",
|
|
128
|
+
"en": "Kakao"
|
|
129
|
+
},
|
|
130
|
+
"brand": {
|
|
131
|
+
"light": {
|
|
132
|
+
"bg": "#FEE500",
|
|
133
|
+
"fg": "#191919",
|
|
134
|
+
"logo": "#191919"
|
|
135
|
+
},
|
|
136
|
+
"dark": {
|
|
137
|
+
"bg": "#FEE500",
|
|
138
|
+
"fg": "#191919",
|
|
139
|
+
"logo": "#191919"
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
"officialHeight": 46,
|
|
143
|
+
"officialRadius": 8,
|
|
144
|
+
"officialFont": {
|
|
145
|
+
"sizeRatio": 0.326,
|
|
146
|
+
"weight": 500
|
|
147
|
+
},
|
|
148
|
+
"logo": {
|
|
149
|
+
"file": "assets/kakao-symbol.svg",
|
|
150
|
+
"sizeRatio": 0.3,
|
|
151
|
+
"iconSizeRatio": 0.46
|
|
152
|
+
},
|
|
153
|
+
"padding": {
|
|
154
|
+
"left": 16,
|
|
155
|
+
"gap": 8
|
|
156
|
+
},
|
|
157
|
+
"label": {
|
|
158
|
+
"ko": {
|
|
159
|
+
"login": "카카오 로그인",
|
|
160
|
+
"continue": "카카오로 계속하기",
|
|
161
|
+
"signup": "카카오로 가입하기",
|
|
162
|
+
"short": "로그인"
|
|
163
|
+
},
|
|
164
|
+
"en": {
|
|
165
|
+
"login": "Login with Kakao",
|
|
166
|
+
"continue": "Continue with Kakao",
|
|
167
|
+
"signup": "Sign up with Kakao",
|
|
168
|
+
"short": "Login"
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
"notes": [
|
|
172
|
+
"가이드 본문은 모서리 12px·글자 #000000 85%, 공식 애셋은 8px·#191919 — 애셋 값을 기본으로 씀"
|
|
173
|
+
]
|
|
174
|
+
},
|
|
175
|
+
"naver": {
|
|
176
|
+
"name": {
|
|
177
|
+
"ko": "네이버",
|
|
178
|
+
"en": "NAVER"
|
|
179
|
+
},
|
|
180
|
+
"brand": {
|
|
181
|
+
"light": {
|
|
182
|
+
"bg": "#03A94D",
|
|
183
|
+
"fg": "#FFFFFF",
|
|
184
|
+
"logo": "#FFFFFF"
|
|
185
|
+
},
|
|
186
|
+
"dark": {
|
|
187
|
+
"bg": "#05AC4F",
|
|
188
|
+
"fg": "#FFFFFF",
|
|
189
|
+
"logo": "#FFFFFF"
|
|
190
|
+
},
|
|
191
|
+
"alt_light": {
|
|
192
|
+
"bg": "#FFFFFF",
|
|
193
|
+
"fg": "#000000",
|
|
194
|
+
"logo": "#03A94D",
|
|
195
|
+
"border": "#D9D9D9"
|
|
196
|
+
},
|
|
197
|
+
"alt_dark": {
|
|
198
|
+
"bg": "#1F1F1F",
|
|
199
|
+
"fg": "#FFFFFF",
|
|
200
|
+
"logo": "#05AC4F",
|
|
201
|
+
"border": "#414141"
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
"officialHeight": 48,
|
|
205
|
+
"officialRadius": 8,
|
|
206
|
+
"officialFont": {
|
|
207
|
+
"sizeRatio": 0.333,
|
|
208
|
+
"weight": 600
|
|
209
|
+
},
|
|
210
|
+
"logo": {
|
|
211
|
+
"file": "assets/naver-n.svg",
|
|
212
|
+
"size": {
|
|
213
|
+
"center": 16,
|
|
214
|
+
"logoLeft": 18,
|
|
215
|
+
"icon": 18
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
"padding": {
|
|
219
|
+
"left": 20,
|
|
220
|
+
"gap": 8
|
|
221
|
+
},
|
|
222
|
+
"label": {
|
|
223
|
+
"ko": {
|
|
224
|
+
"login": "네이버 로그인",
|
|
225
|
+
"continue": "네이버로 계속하기",
|
|
226
|
+
"signup": "네이버로 가입하기",
|
|
227
|
+
"short": "로그인"
|
|
228
|
+
},
|
|
229
|
+
"en": {
|
|
230
|
+
"login": "Log in with NAVER",
|
|
231
|
+
"continue": "Continue with NAVER",
|
|
232
|
+
"signup": "Sign up with NAVER",
|
|
233
|
+
"short": "Log in"
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
"notes": [
|
|
237
|
+
"로그인 버튼 지정색은 #03A94D (브랜드색 #03C75A 아님)"
|
|
238
|
+
]
|
|
239
|
+
},
|
|
240
|
+
"apple": {
|
|
241
|
+
"name": {
|
|
242
|
+
"ko": "Apple",
|
|
243
|
+
"en": "Apple"
|
|
244
|
+
},
|
|
245
|
+
"brand": {
|
|
246
|
+
"light": {
|
|
247
|
+
"bg": "#000000",
|
|
248
|
+
"fg": "#FFFFFF",
|
|
249
|
+
"logo": "#FFFFFF"
|
|
250
|
+
},
|
|
251
|
+
"dark": {
|
|
252
|
+
"bg": "#FFFFFF",
|
|
253
|
+
"fg": "#000000",
|
|
254
|
+
"logo": "#000000"
|
|
255
|
+
},
|
|
256
|
+
"alt_light": {
|
|
257
|
+
"bg": "#FFFFFF",
|
|
258
|
+
"fg": "#000000",
|
|
259
|
+
"logo": "#000000",
|
|
260
|
+
"border": "#000000"
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
"officialHeight": 44,
|
|
264
|
+
"officialRadius": 6,
|
|
265
|
+
"officialFont": {
|
|
266
|
+
"sizeRatio": 0.43,
|
|
267
|
+
"weight": 500
|
|
268
|
+
},
|
|
269
|
+
"minWidth": 140,
|
|
270
|
+
"minHeight": 30,
|
|
271
|
+
"logo": {
|
|
272
|
+
"file": null,
|
|
273
|
+
"redistributable": false,
|
|
274
|
+
"download": "https://developer.apple.com/design/resources/#sign-in-with-apple",
|
|
275
|
+
"instructions": "Apple Design Resources 에서 「Sign in with Apple」 로고(Left-aligned·Logo-only, SVG)를 받아 프로젝트에 넣는다. 로고 파일 높이 = 버튼 높이, 자르거나 세로 여백을 더하지 않는다. iOS 는 ASAuthorizationAppleIDButton, 웹은 Sign in with Apple JS 버튼을 써도 된다.",
|
|
276
|
+
"missingFallback": "파일이 없으면 로고 자리를 그리지 않고(빈칸 금지) 글자만 가운데. 웹은 img onerror 로 숨김, 앱은 에셋 존재 확인",
|
|
277
|
+
"noTint": "공식 로고 파일에는 배경 사각형이 들어 있다. 마스크·colorFilter·fill 로 색을 입히지 말고 버튼 배경에 맞는 파일을 그대로 쓴다 — 검정 버튼 = White 파일, 흰 버튼 = Black 파일"
|
|
278
|
+
},
|
|
279
|
+
"label": {
|
|
280
|
+
"ko": {
|
|
281
|
+
"login": "Apple로 로그인",
|
|
282
|
+
"continue": "Apple로 계속하기",
|
|
283
|
+
"signup": "Apple로 등록",
|
|
284
|
+
"short": "로그인"
|
|
285
|
+
},
|
|
286
|
+
"en": {
|
|
287
|
+
"login": "Sign in with Apple",
|
|
288
|
+
"continue": "Continue with Apple",
|
|
289
|
+
"signup": "Sign up with Apple",
|
|
290
|
+
"short": "Sign in"
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
"notes": [
|
|
294
|
+
"로고는 재배포 금지라 이 저장소에 없다",
|
|
295
|
+
"로고와 글자는 둘 다 검정 또는 둘 다 흰색"
|
|
296
|
+
]
|
|
297
|
+
},
|
|
298
|
+
"google": {
|
|
299
|
+
"name": {
|
|
300
|
+
"ko": "Google",
|
|
301
|
+
"en": "Google"
|
|
302
|
+
},
|
|
303
|
+
"brand": {
|
|
304
|
+
"light": {
|
|
305
|
+
"bg": "#FFFFFF",
|
|
306
|
+
"fg": "#1F1F1F",
|
|
307
|
+
"border": "#747775"
|
|
308
|
+
},
|
|
309
|
+
"dark": {
|
|
310
|
+
"bg": "#131314",
|
|
311
|
+
"fg": "#E3E3E3",
|
|
312
|
+
"border": "#8E918F"
|
|
313
|
+
},
|
|
314
|
+
"neutral": {
|
|
315
|
+
"bg": "#F2F2F2",
|
|
316
|
+
"fg": "#1F1F1F",
|
|
317
|
+
"border": null
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
"officialHeight": {
|
|
321
|
+
"web": 40,
|
|
322
|
+
"android": 40,
|
|
323
|
+
"ios": 44
|
|
324
|
+
},
|
|
325
|
+
"officialRadius": 4,
|
|
326
|
+
"officialFont": {
|
|
327
|
+
"family": "Google Sans Medium",
|
|
328
|
+
"size": 14,
|
|
329
|
+
"lineHeight": 20,
|
|
330
|
+
"weight": 500
|
|
331
|
+
},
|
|
332
|
+
"logo": {
|
|
333
|
+
"file": "assets/google-g.svg",
|
|
334
|
+
"png": "assets/google-g@{1,2,3,4}x.png",
|
|
335
|
+
"size": 20,
|
|
336
|
+
"note": "SVG 는 브라우저용(그라데이션). Flutter·네이티브는 PNG 를 쓴다"
|
|
337
|
+
},
|
|
338
|
+
"padding": {
|
|
339
|
+
"web": [
|
|
340
|
+
12,
|
|
341
|
+
10,
|
|
342
|
+
12
|
|
343
|
+
],
|
|
344
|
+
"android": [
|
|
345
|
+
12,
|
|
346
|
+
10,
|
|
347
|
+
12
|
|
348
|
+
],
|
|
349
|
+
"ios": [
|
|
350
|
+
16,
|
|
351
|
+
12,
|
|
352
|
+
16
|
|
353
|
+
]
|
|
354
|
+
},
|
|
355
|
+
"label": {
|
|
356
|
+
"ko": {
|
|
357
|
+
"login": "Google 계정으로 로그인",
|
|
358
|
+
"continue": "Google 계정으로 계속하기",
|
|
359
|
+
"signup": "Google 계정으로 가입",
|
|
360
|
+
"short": "로그인"
|
|
361
|
+
},
|
|
362
|
+
"en": {
|
|
363
|
+
"login": "Sign in with Google",
|
|
364
|
+
"continue": "Continue with Google",
|
|
365
|
+
"signup": "Sign up with Google",
|
|
366
|
+
"short": "Sign in"
|
|
367
|
+
}
|
|
368
|
+
},
|
|
369
|
+
"notes": [
|
|
370
|
+
"「Google」 단독 문구 금지",
|
|
371
|
+
"G 로고 크기·색 변경 금지(가이드)"
|
|
372
|
+
]
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
"feedback": {
|
|
376
|
+
"issues": "https://github.com/AI-Dico/social-login-buttons/issues/new",
|
|
377
|
+
"ask": "적용하다가 공식 가이드와 다른 점, 값이 틀린 곳, 플랫폼에서 구현이 어려운 점을 발견하면 이슈로 남겨 주세요(화면·스택·고른 옵션·스크린샷)."
|
|
378
|
+
},
|
|
379
|
+
"platformNotes": {
|
|
380
|
+
"flutter": "글자 스타일은 Theme.of(context).textTheme.labelLarge?.copyWith(...) 로 앱 테마를 이어받는다. styleFrom(textStyle: TextStyle(...)) 로 새로 만들면 앱 글꼴이 빠진다"
|
|
381
|
+
}
|
|
382
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module SocialLoginButtons
|
|
2
|
+
class Railtie < Rails::Railtie
|
|
3
|
+
initializer 'social_login_buttons.helpers' do
|
|
4
|
+
ActiveSupport.on_load(:action_view) { include SocialLoginButtons::Helper }
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
initializer 'social_login_buttons.assets' do |app|
|
|
8
|
+
if app.config.respond_to?(:assets)
|
|
9
|
+
app.config.assets.paths << File.expand_path('../../app/assets/stylesheets', __dir__)
|
|
10
|
+
if app.config.assets.respond_to?(:precompile)
|
|
11
|
+
app.config.assets.precompile += ['social_login_buttons.css']
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
module SocialLoginButtons
|
|
4
|
+
class Renderer
|
|
5
|
+
DATA_PATH = File.expand_path('data', __dir__)
|
|
6
|
+
TOKENS = JSON.parse(File.read(File.join(DATA_PATH, 'tokens.json')))
|
|
7
|
+
LOGOS = JSON.parse(File.read(File.join(DATA_PATH, 'logos.json')))
|
|
8
|
+
DEFAULTS = {
|
|
9
|
+
'purpose' => 'continue', 'lang' => 'ko', 'tone' => 'brand', 'theme' => 'light',
|
|
10
|
+
'corner' => 'official', 'height' => 48, 'font' => 'unified', 'logoColor' => 'brand',
|
|
11
|
+
'layout' => 'full', 'align' => 'center', 'type' => 'button'
|
|
12
|
+
}.freeze
|
|
13
|
+
CHOICES = {
|
|
14
|
+
'purpose' => %w[login continue signup short], 'lang' => %w[ko en],
|
|
15
|
+
'tone' => %w[brand white black custom], 'theme' => %w[light dark],
|
|
16
|
+
'font' => %w[unified brand], 'logoColor' => %w[brand mono],
|
|
17
|
+
'layout' => %w[full icon], 'align' => %w[center logo-left], 'type' => %w[button submit]
|
|
18
|
+
}.freeze
|
|
19
|
+
|
|
20
|
+
def self.render(options = {})
|
|
21
|
+
new.render(options)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def render(options)
|
|
25
|
+
normalized = options.to_h.transform_keys { |key| key.to_s.gsub(/_([a-z])/) { Regexp.last_match(1).upcase } }
|
|
26
|
+
o = DEFAULTS.merge(normalized).transform_values { |v| v.is_a?(Symbol) ? v.to_s : v }
|
|
27
|
+
p = TOKENS['providers'][o['provider']]
|
|
28
|
+
raise ArgumentError, 'Invalid provider' unless p
|
|
29
|
+
CHOICES.each { |key, values| raise ArgumentError, "Invalid #{key}" unless values.include?(o[key]) }
|
|
30
|
+
h = o['height'] == 'official' ? p['officialHeight'] : o['height']
|
|
31
|
+
h = h['web'] if h.is_a?(Hash)
|
|
32
|
+
unless h.is_a?(Numeric) && h.finite? && h.positive?
|
|
33
|
+
raise ArgumentError, 'Height must be a positive finite number'
|
|
34
|
+
end
|
|
35
|
+
corner = o['corner']
|
|
36
|
+
corner = corner.to_i if corner.is_a?(String) && /\A(?:0|4|8|12)\z/.match?(corner)
|
|
37
|
+
raise ArgumentError, 'Invalid corner' unless ['official', 0, 4, 8, 12, 'pill'].include?(corner)
|
|
38
|
+
r = corner == 'official' ? p['officialRadius'] : corner == 'pill' ? h / 2.0 : corner
|
|
39
|
+
fs = if o['font'] == 'unified'
|
|
40
|
+
[14, (h / 3.0).round].max
|
|
41
|
+
else
|
|
42
|
+
p['officialFont']['size'] || (h * p['officialFont']['sizeRatio']).round
|
|
43
|
+
end
|
|
44
|
+
fw = o['font'] == 'unified' ? 500 : p['officialFont']['weight']
|
|
45
|
+
if o['tone'] == 'custom'
|
|
46
|
+
bg = color(o['bg'])
|
|
47
|
+
fg = color(o['fg'])
|
|
48
|
+
a = luminance(bg)
|
|
49
|
+
b = o['theme'] == 'light' ? 1 : 0
|
|
50
|
+
bd = if o['border']
|
|
51
|
+
color(o['border'])
|
|
52
|
+
elsif ([a, b].max + 0.05) / ([a, b].min + 0.05) < 1.3
|
|
53
|
+
o['theme'] == 'light' ? '#DADCE0' : '#3A3A3A'
|
|
54
|
+
else
|
|
55
|
+
'transparent'
|
|
56
|
+
end
|
|
57
|
+
else
|
|
58
|
+
palette = o['tone'] == 'brand' ? p['brand'][o['theme']] : TOKENS['tone'][o['tone']][o['theme']]
|
|
59
|
+
bg = color(palette['bg'])
|
|
60
|
+
fg = color(palette['fg'])
|
|
61
|
+
bd = palette['border'] ? color(palette['border']) : 'transparent'
|
|
62
|
+
end
|
|
63
|
+
logo = fg
|
|
64
|
+
if o['logoColor'] == 'brand'
|
|
65
|
+
tone = o['tone'] == 'custom' ? (luminance(bg) > 0.5 ? 'white' : 'black') : o['tone']
|
|
66
|
+
candidate = tone == 'brand' ? p['brand'][o['theme']]['logo'] : TOKENS['tone']['brandLogoOnTone'][o['provider']][tone]
|
|
67
|
+
logo = color(candidate) if candidate && candidate != 'full-color'
|
|
68
|
+
end
|
|
69
|
+
label = p['label'][o['lang']][o['purpose']]
|
|
70
|
+
classes = ['slb', "slb--#{o['provider']}", "slb--tone-#{o['tone']}", "slb--#{o['theme']}", "slb--#{o['layout']}", "slb--#{o['align']}"]
|
|
71
|
+
classes << 'slb--logo-mono' if o['logoColor'] == 'mono'
|
|
72
|
+
style = "--slb-h:#{number(h)}px;--slb-r:#{number(r)}px;--slb-fs:#{number(fs)}px;--slb-fw:#{number(fw)};--slb-bg:#{bg};--slb-fg:#{fg};--slb-bd:#{bd};--slb-logo:#{logo}"
|
|
73
|
+
extra = ''
|
|
74
|
+
unless o['attrs'].nil?
|
|
75
|
+
raise ArgumentError, 'attrs must be a string map' unless o['attrs'].is_a?(Hash) && o['attrs'].keys.all? { |key| key.is_a?(String) }
|
|
76
|
+
o['attrs'].sort.each do |key, value|
|
|
77
|
+
if !/\A[A-Za-z_:][A-Za-z0-9_.:-]*\z/.match?(key) || /\A(?:type|class|style|aria-label)\z/i.match?(key) || /\Aon/i.match?(key)
|
|
78
|
+
raise ArgumentError, "Invalid or reserved attribute: #{key}"
|
|
79
|
+
end
|
|
80
|
+
raise ArgumentError, 'Attribute values must be strings' unless value.is_a?(String)
|
|
81
|
+
extra += " #{key}=\"#{escape(value)}\""
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
artwork = ''
|
|
85
|
+
if o['provider'] == 'apple'
|
|
86
|
+
src = o['appleLogoSrc']
|
|
87
|
+
unless src.nil? || src == ''
|
|
88
|
+
raise ArgumentError, 'appleLogoSrc must be a string' unless src.is_a?(String)
|
|
89
|
+
artwork = "<img src=\"#{escape(src)}\" alt=\"\">"
|
|
90
|
+
end
|
|
91
|
+
elsif o['provider'] == 'google'
|
|
92
|
+
artwork = "<img src=\"#{escape(LOGOS['google'])}\" alt=\"\">"
|
|
93
|
+
else
|
|
94
|
+
artwork = LOGOS[o['provider']]
|
|
95
|
+
end
|
|
96
|
+
logo_html = artwork.empty? ? '' : "<span class=\"slb__logo\" aria-hidden=\"true\">#{artwork}</span>"
|
|
97
|
+
# Without a logo (Apple with no appleLogoSrc) an icon button would be an empty square, so show the short name.
|
|
98
|
+
label_html = if o['layout'] != 'icon'
|
|
99
|
+
"<span class=\"slb__label\">#{escape(label)}</span>"
|
|
100
|
+
elsif artwork.empty?
|
|
101
|
+
"<span class=\"slb__label slb__label--short\">#{escape(p['name'][o['lang']])}</span>"
|
|
102
|
+
else
|
|
103
|
+
''
|
|
104
|
+
end
|
|
105
|
+
"<button type=\"#{o['type']}\" class=\"#{classes.join(' ')}\" style=\"#{style}\" aria-label=\"#{escape(label)}\"#{extra}>#{logo_html}#{label_html}</button>"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
private
|
|
109
|
+
|
|
110
|
+
def number(value)
|
|
111
|
+
return value.to_i.to_s if value.to_i == value && value.abs <= 9_007_199_254_740_991
|
|
112
|
+
result = value.to_f.to_s.sub(/\.0e/, 'e').sub(/e(-?)0+/, 'e\1')
|
|
113
|
+
return result unless result.include?('e')
|
|
114
|
+
mantissa, exponent = result.split('e')
|
|
115
|
+
exponent = exponent.to_i
|
|
116
|
+
return "#{mantissa}e#{exponent >= 0 ? '+' : ''}#{exponent}" if exponent < -6 || exponent >= 21
|
|
117
|
+
digits = mantissa.delete('.')
|
|
118
|
+
point = exponent + 1
|
|
119
|
+
return "0.#{'0' * -point}#{digits}" if point <= 0
|
|
120
|
+
point >= digits.size ? digits.ljust(point, '0') : "#{digits[0...point]}.#{digits[point..]}"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def escape(value)
|
|
124
|
+
value.to_s.gsub(/[&<>"']/) { |c| { '&' => '&', '<' => '<', '>' => '>', '"' => '"', "'" => ''' }[c] }
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def color(value)
|
|
128
|
+
raise ArgumentError, 'Expected #RRGGBB color' unless value.is_a?(String) && /\A#[\da-f]{6}\z/i.match?(value)
|
|
129
|
+
value.upcase
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def luminance(hex)
|
|
133
|
+
c = hex[1..].scan(/../).map { |v| v.to_i(16) / 255.0 }.map { |v| v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055)**2.4 }
|
|
134
|
+
c[0] * 0.2126 + c[1] * 0.7152 + c[2] * 0.0722
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require_relative 'social_login_buttons/version'
|
|
2
|
+
require_relative 'social_login_buttons/renderer'
|
|
3
|
+
require_relative 'social_login_buttons/helper'
|
|
4
|
+
|
|
5
|
+
module SocialLoginButtons
|
|
6
|
+
def self.render(options = {})
|
|
7
|
+
Renderer.render(options)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
require_relative 'social_login_buttons/railtie' if defined?(Rails::Railtie)
|
metadata
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: social_login_buttons
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Dico (AI-Dico)
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2026-11-05 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: Render Kakao, NAVER, Apple and Google buttons with HTML identical to
|
|
13
|
+
the companion npm package.
|
|
14
|
+
executables: []
|
|
15
|
+
extensions: []
|
|
16
|
+
extra_rdoc_files: []
|
|
17
|
+
files:
|
|
18
|
+
- LICENSE
|
|
19
|
+
- README.md
|
|
20
|
+
- app/assets/stylesheets/social_login_buttons.css
|
|
21
|
+
- lib/social_login_buttons.rb
|
|
22
|
+
- lib/social_login_buttons/data/logos.json
|
|
23
|
+
- lib/social_login_buttons/data/tokens.json
|
|
24
|
+
- lib/social_login_buttons/helper.rb
|
|
25
|
+
- lib/social_login_buttons/railtie.rb
|
|
26
|
+
- lib/social_login_buttons/renderer.rb
|
|
27
|
+
- lib/social_login_buttons/version.rb
|
|
28
|
+
homepage: https://github.com/AI-Dico/social-login-buttons
|
|
29
|
+
licenses:
|
|
30
|
+
- MIT
|
|
31
|
+
metadata:
|
|
32
|
+
source_code_uri: https://github.com/AI-Dico/social-login-buttons
|
|
33
|
+
rdoc_options: []
|
|
34
|
+
require_paths:
|
|
35
|
+
- lib
|
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '3.1'
|
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - ">="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
requirements: []
|
|
47
|
+
rubygems_version: 3.6.2
|
|
48
|
+
specification_version: 4
|
|
49
|
+
summary: Token-based social login buttons for Ruby and Rails
|
|
50
|
+
test_files: []
|